2
									
								
								.github/workflows/development-build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/development-build.yml
									
									
									
									
										vendored
									
									
								
							@@ -52,7 +52,7 @@ jobs:
 | 
				
			|||||||
            push: true
 | 
					            push: true
 | 
				
			||||||
            tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64
 | 
					            tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64
 | 
				
			||||||
  merge-manifest:
 | 
					  merge-manifest:
 | 
				
			||||||
      runs-on: [self-hosted, x64]
 | 
					      runs-on: ubuntu-latest
 | 
				
			||||||
      permissions:
 | 
					      permissions:
 | 
				
			||||||
        contents: read
 | 
					        contents: read
 | 
				
			||||||
        packages: write
 | 
					        packages: write
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.github/workflows/production-build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/production-build.yml
									
									
									
									
										vendored
									
									
								
							@@ -10,7 +10,7 @@ env:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  amd64:
 | 
					  amd64:
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: [self-hosted, x64]
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v3
 | 
					      - uses: actions/checkout@v3
 | 
				
			||||||
      - name: Login to ghcr.io
 | 
					      - name: Login to ghcr.io
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,7 +73,7 @@ class RunRemoteProcess
 | 
				
			|||||||
        $this->time_start = hrtime(true);
 | 
					        $this->time_start = hrtime(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $status = ProcessStatus::IN_PROGRESS;
 | 
					        $status = ProcessStatus::IN_PROGRESS;
 | 
				
			||||||
        $processResult = processWithEnv()->forever()->run($this->getCommand(), $this->handleOutput(...));
 | 
					        $processResult = Process::forever()->run($this->getCommand(), $this->handleOutput(...));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
 | 
					        if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
 | 
				
			||||||
            $status = ProcessStatus::ERROR;
 | 
					            $status = ProcessStatus::ERROR;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,10 +16,7 @@ class CheckConfigurationSync
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if ($reset || is_null($proxy_configuration)) {
 | 
					        if ($reset || is_null($proxy_configuration)) {
 | 
				
			||||||
            $proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
 | 
					            $proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
 | 
				
			||||||
            resolve(SaveConfigurationSync::class)($server, $proxy_configuration);
 | 
					 | 
				
			||||||
            return $proxy_configuration;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return $proxy_configuration;
 | 
					        return $proxy_configuration;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,11 +7,12 @@ use Illuminate\Support\Str;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class SaveConfigurationSync
 | 
					class SaveConfigurationSync
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public function __invoke(Server $server, string $configuration)
 | 
					    public function __invoke(Server $server)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 | 
					            $proxy_settings = resolve(CheckConfigurationSync::class)($server, true);
 | 
				
			||||||
            $proxy_path = get_proxy_path();
 | 
					            $proxy_path = get_proxy_path();
 | 
				
			||||||
            $docker_compose_yml_base64 = base64_encode($configuration);
 | 
					            $docker_compose_yml_base64 = base64_encode($proxy_settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
 | 
					            $server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
 | 
				
			||||||
            $server->save();
 | 
					            $server->save();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ use Illuminate\Console\Command;
 | 
				
			|||||||
use Illuminate\Mail\Message;
 | 
					use Illuminate\Mail\Message;
 | 
				
			||||||
use Illuminate\Notifications\Messages\MailMessage;
 | 
					use Illuminate\Notifications\Messages\MailMessage;
 | 
				
			||||||
use Mail;
 | 
					use Mail;
 | 
				
			||||||
use Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use function Laravel\Prompts\confirm;
 | 
					use function Laravel\Prompts\confirm;
 | 
				
			||||||
use function Laravel\Prompts\select;
 | 
					use function Laravel\Prompts\select;
 | 
				
			||||||
@@ -62,7 +62,7 @@ class Emails extends Command
 | 
				
			|||||||
                'application-status-changed' => 'Application - Status Changed',
 | 
					                'application-status-changed' => 'Application - Status Changed',
 | 
				
			||||||
                'backup-success' => 'Database - Backup Success',
 | 
					                'backup-success' => 'Database - Backup Success',
 | 
				
			||||||
                'backup-failed' => 'Database - Backup Failed',
 | 
					                'backup-failed' => 'Database - Backup Failed',
 | 
				
			||||||
                'invitation-link' => 'Invitation Link',
 | 
					                // 'invitation-link' => 'Invitation Link',
 | 
				
			||||||
                'waitlist-invitation-link' => 'Waitlist Invitation Link',
 | 
					                'waitlist-invitation-link' => 'Waitlist Invitation Link',
 | 
				
			||||||
                'waitlist-confirmation' => 'Waitlist Confirmation',
 | 
					                'waitlist-confirmation' => 'Waitlist Confirmation',
 | 
				
			||||||
                'realusers-before-trial' => 'REAL - Registered Users Before Trial without Subscription',
 | 
					                'realusers-before-trial' => 'REAL - Registered Users Before Trial without Subscription',
 | 
				
			||||||
@@ -141,20 +141,20 @@ class Emails extends Command
 | 
				
			|||||||
                $this->mail = (new BackupSuccess($backup, $db))->toMail();
 | 
					                $this->mail = (new BackupSuccess($backup, $db))->toMail();
 | 
				
			||||||
                $this->sendEmail();
 | 
					                $this->sendEmail();
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'invitation-link':
 | 
					            // case 'invitation-link':
 | 
				
			||||||
                $user = User::all()->first();
 | 
					            //     $user = User::all()->first();
 | 
				
			||||||
                $invitation = TeamInvitation::whereEmail($user->email)->first();
 | 
					            //     $invitation = TeamInvitation::whereEmail($user->email)->first();
 | 
				
			||||||
                if (!$invitation) {
 | 
					            //     if (!$invitation) {
 | 
				
			||||||
                    $invitation = TeamInvitation::create([
 | 
					            //         $invitation = TeamInvitation::create([
 | 
				
			||||||
                        'uuid' => Str::uuid(),
 | 
					            //             'uuid' => Str::uuid(),
 | 
				
			||||||
                        'email' => $user->email,
 | 
					            //             'email' => $user->email,
 | 
				
			||||||
                        'team_id' => 1,
 | 
					            //             'team_id' => 1,
 | 
				
			||||||
                        'link' => 'http://example.com',
 | 
					            //             'link' => 'http://example.com',
 | 
				
			||||||
                    ]);
 | 
					            //         ]);
 | 
				
			||||||
                }
 | 
					            //     }
 | 
				
			||||||
                $this->mail = (new InvitationLink($user))->toMail();
 | 
					            //     $this->mail = (new InvitationLink($user))->toMail();
 | 
				
			||||||
                $this->sendEmail();
 | 
					            //     $this->sendEmail();
 | 
				
			||||||
                break;
 | 
					            //     break;
 | 
				
			||||||
            case 'waitlist-invitation-link':
 | 
					            case 'waitlist-invitation-link':
 | 
				
			||||||
                $this->mail = new MailMessage();
 | 
					                $this->mail = new MailMessage();
 | 
				
			||||||
                $this->mail->view('emails.waitlist-invitation', [
 | 
					                $this->mail->view('emails.waitlist-invitation', [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ class Kernel extends ConsoleKernel
 | 
				
			|||||||
        if (isDev()) {
 | 
					        if (isDev()) {
 | 
				
			||||||
            // $schedule->job(new ContainerStatusJob(Server::find(0)))->everyTenMinutes()->onOneServer();
 | 
					            // $schedule->job(new ContainerStatusJob(Server::find(0)))->everyTenMinutes()->onOneServer();
 | 
				
			||||||
            // $schedule->command('horizon:snapshot')->everyMinute();
 | 
					            // $schedule->command('horizon:snapshot')->everyMinute();
 | 
				
			||||||
            // $schedule->job(new CleanupInstanceStuffsJob)->everyMinute();
 | 
					            $schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
 | 
				
			||||||
            // $schedule->job(new CheckResaleLicenseJob)->hourly();
 | 
					            // $schedule->job(new CheckResaleLicenseJob)->hourly();
 | 
				
			||||||
            // $schedule->job(new DockerCleanupJob)->everyOddHour();
 | 
					            // $schedule->job(new DockerCleanupJob)->everyOddHour();
 | 
				
			||||||
            // $this->instance_auto_update($schedule);
 | 
					            // $this->instance_auto_update($schedule);
 | 
				
			||||||
@@ -29,7 +29,7 @@ class Kernel extends ConsoleKernel
 | 
				
			|||||||
            $this->check_resources($schedule);
 | 
					            $this->check_resources($schedule);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $schedule->command('horizon:snapshot')->everyFiveMinutes();
 | 
					            $schedule->command('horizon:snapshot')->everyFiveMinutes();
 | 
				
			||||||
            $schedule->job(new CleanupInstanceStuffsJob)->everyTenMinutes()->onOneServer();
 | 
					            $schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
 | 
				
			||||||
            $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
 | 
					            $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
 | 
				
			||||||
            $schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
 | 
					            $schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
 | 
				
			||||||
            $this->instance_auto_update($schedule);
 | 
					            $this->instance_auto_update($schedule);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,21 +3,18 @@
 | 
				
			|||||||
namespace App\Http\Controllers;
 | 
					namespace App\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Models\InstanceSettings;
 | 
					use App\Models\InstanceSettings;
 | 
				
			||||||
use App\Models\Project;
 | 
					 | 
				
			||||||
use App\Models\S3Storage;
 | 
					use App\Models\S3Storage;
 | 
				
			||||||
use App\Models\StandalonePostgresql;
 | 
					use App\Models\StandalonePostgresql;
 | 
				
			||||||
use App\Models\TeamInvitation;
 | 
					use App\Models\TeamInvitation;
 | 
				
			||||||
use App\Models\User;
 | 
					use App\Models\User;
 | 
				
			||||||
use Auth;
 | 
					 | 
				
			||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
 | 
					use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
 | 
				
			||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
 | 
					use Illuminate\Foundation\Validation\ValidatesRequests;
 | 
				
			||||||
use Illuminate\Routing\Controller as BaseController;
 | 
					use Illuminate\Routing\Controller as BaseController;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Auth;
 | 
				
			||||||
use Illuminate\Support\Facades\Crypt;
 | 
					use Illuminate\Support\Facades\Crypt;
 | 
				
			||||||
use Illuminate\Support\Facades\Hash;
 | 
					use Illuminate\Support\Facades\Hash;
 | 
				
			||||||
use Illuminate\Support\Facades\Http;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
use Throwable;
 | 
					use Throwable;
 | 
				
			||||||
use Str;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller extends BaseController
 | 
					class Controller extends BaseController
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -35,8 +32,15 @@ class Controller extends BaseController
 | 
				
			|||||||
                return redirect()->route('login');
 | 
					                return redirect()->route('login');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (Hash::check($password, $user->password)) {
 | 
					            if (Hash::check($password, $user->password)) {
 | 
				
			||||||
                Auth::login($user);
 | 
					                $invitation = TeamInvitation::whereEmail($email);
 | 
				
			||||||
 | 
					                if ($invitation->exists()) {
 | 
				
			||||||
 | 
					                    $team = $invitation->first()->team;
 | 
				
			||||||
 | 
					                    $user->teams()->attach($team->id, ['role' => $invitation->first()->role]);
 | 
				
			||||||
 | 
					                    $invitation->delete();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
                    $team = $user->teams()->first();
 | 
					                    $team = $user->teams()->first();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Auth::login($user);
 | 
				
			||||||
                session(['currentTeam' => $team]);
 | 
					                session(['currentTeam' => $team]);
 | 
				
			||||||
                return redirect()->route('dashboard');
 | 
					                return redirect()->route('dashboard');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -137,24 +141,20 @@ class Controller extends BaseController
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
 | 
					            $invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
 | 
				
			||||||
            $user = User::whereEmail($invitation->email)->firstOrFail();
 | 
					            $user = User::whereEmail($invitation->email)->firstOrFail();
 | 
				
			||||||
            if (is_null(auth()->user())) {
 | 
					 | 
				
			||||||
                return redirect()->route('login');
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (auth()->user()->id !== $user->id) {
 | 
					            if (auth()->user()->id !== $user->id) {
 | 
				
			||||||
                abort(401);
 | 
					                abort(401);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            $invitationValid = $invitation->isValid();
 | 
				
			||||||
            $createdAt = $invitation->created_at;
 | 
					            if ($invitationValid) {
 | 
				
			||||||
            $diff = $createdAt->diffInMinutes(now());
 | 
					 | 
				
			||||||
            if ($diff <= config('constants.invitation.link.expiration')) {
 | 
					 | 
				
			||||||
                $user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
 | 
					                $user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
 | 
				
			||||||
 | 
					                refreshSession($invitation->team);
 | 
				
			||||||
                $invitation->delete();
 | 
					                $invitation->delete();
 | 
				
			||||||
                return redirect()->route('team.index');
 | 
					                return redirect()->route('team.index');
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                $invitation->delete();
 | 
					 | 
				
			||||||
                abort(401);
 | 
					                abort(401);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (Throwable $e) {
 | 
					        } catch (Throwable $e) {
 | 
				
			||||||
 | 
					            ray($e->getMessage());
 | 
				
			||||||
            throw $e;
 | 
					            throw $e;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ class Help extends Component
 | 
				
			|||||||
    ];
 | 
					    ];
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->path = Route::current()->uri();
 | 
					        $this->path = Route::current()?->uri() ?? null;
 | 
				
			||||||
        if (isDev()) {
 | 
					        if (isDev()) {
 | 
				
			||||||
            $this->description = "I'm having trouble with {$this->path}";
 | 
					            $this->description = "I'm having trouble with {$this->path}";
 | 
				
			||||||
            $this->subject = "Help with {$this->path}";
 | 
					            $this->subject = "Help with {$this->path}";
 | 
				
			||||||
@@ -41,7 +41,7 @@ class Help extends Component
 | 
				
			|||||||
                ]
 | 
					                ]
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            $mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
 | 
					            $mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
 | 
				
			||||||
            send_user_an_email($mail, 'hi@coollabs.io', auth()->user()?->email);
 | 
					            send_user_an_email($mail,  auth()->user()?->email, 'hi@coollabs.io');
 | 
				
			||||||
            $this->emit('success', 'Your message has been sent successfully. We will get in touch with you as soon as possible.');
 | 
					            $this->emit('success', 'Your message has been sent successfully. We will get in touch with you as soon as possible.');
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return general_error_handler($e, $this);
 | 
					            return general_error_handler($e, $this);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,9 +46,6 @@ class DiscordSettings extends Component
 | 
				
			|||||||
    public function saveModel()
 | 
					    public function saveModel()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->team->save();
 | 
					        $this->team->save();
 | 
				
			||||||
        if (is_a($this->team, Team::class)) {
 | 
					 | 
				
			||||||
            refreshSession();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        $this->emit('success', 'Settings saved.');
 | 
					        $this->emit('success', 'Settings saved.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,9 +110,6 @@ class EmailSettings extends Component
 | 
				
			|||||||
    public function saveModel()
 | 
					    public function saveModel()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->team->save();
 | 
					        $this->team->save();
 | 
				
			||||||
        if (is_a($this->team, Team::class)) {
 | 
					 | 
				
			||||||
            refreshSession();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        $this->emit('success', 'Settings saved.');
 | 
					        $this->emit('success', 'Settings saved.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
@@ -141,10 +138,11 @@ class EmailSettings extends Component
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $this->resetErrorBag();
 | 
					            $this->resetErrorBag();
 | 
				
			||||||
            $this->validate([
 | 
					            $this->validate([
 | 
				
			||||||
 | 
					                'team.smtp_from_address' => 'required|email',
 | 
				
			||||||
 | 
					                'team.smtp_from_name' => 'required',
 | 
				
			||||||
                'team.resend_api_key' => 'required'
 | 
					                'team.resend_api_key' => 'required'
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
            $this->team->save();
 | 
					            $this->team->save();
 | 
				
			||||||
            refreshSession();
 | 
					 | 
				
			||||||
            $this->emit('success', 'Settings saved successfully.');
 | 
					            $this->emit('success', 'Settings saved successfully.');
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            $this->team->resend_enabled = false;
 | 
					            $this->team->resend_enabled = false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,9 +52,6 @@ class TelegramSettings extends Component
 | 
				
			|||||||
    public function saveModel()
 | 
					    public function saveModel()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->team->save();
 | 
					        $this->team->save();
 | 
				
			||||||
        if (is_a($this->team, Team::class)) {
 | 
					 | 
				
			||||||
            refreshSession();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        $this->emit('success', 'Settings saved.');
 | 
					        $this->emit('success', 'Settings saved.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,13 +4,15 @@ namespace App\Http\Livewire\PrivateKey;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Models\PrivateKey;
 | 
					use App\Models\PrivateKey;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					use phpseclib3\Crypt\PublicKeyLoader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Create extends Component
 | 
					class Create extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public string|null $from = null;
 | 
					    public ?string $from = null;
 | 
				
			||||||
    public string $name;
 | 
					    public string $name;
 | 
				
			||||||
    public string|null $description = null;
 | 
					    public ?string $description = null;
 | 
				
			||||||
    public string $value;
 | 
					    public string $value;
 | 
				
			||||||
 | 
					    public ?string $publicKey = null;
 | 
				
			||||||
    protected $rules = [
 | 
					    protected $rules = [
 | 
				
			||||||
        'name' => 'required|string',
 | 
					        'name' => 'required|string',
 | 
				
			||||||
        'value' => 'required|string',
 | 
					        'value' => 'required|string',
 | 
				
			||||||
@@ -20,6 +22,23 @@ class Create extends Component
 | 
				
			|||||||
        'value' => 'private Key',
 | 
					        'value' => 'private Key',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function generateNewKey()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->name = generate_random_name();
 | 
				
			||||||
 | 
					        $this->description = 'Created by Coolify';
 | 
				
			||||||
 | 
					        ['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function updated($updateProperty)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($updateProperty === 'value') {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                $this->publicKey = PublicKeyLoader::load($this->$updateProperty)->getPublicKey()->toString('OpenSSH',['comment' => '']);
 | 
				
			||||||
 | 
					            } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					                $this->publicKey = "Invalid private key";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->validateOnly($updateProperty);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function createPrivateKey()
 | 
					    public function createPrivateKey()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->validate();
 | 
					        $this->validate();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Http\Livewire\Project\Application;
 | 
					namespace App\Http\Livewire\Project\Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Jobs\ApplicationContainerStatusJob;
 | 
					 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					 | 
				
			||||||
use App\Models\Application;
 | 
					use App\Models\Application;
 | 
				
			||||||
use App\Models\ApplicationPreview;
 | 
					use App\Models\ApplicationPreview;
 | 
				
			||||||
use Illuminate\Support\Collection;
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
 | 
				
			|||||||
use App\Models\EnvironmentVariable;
 | 
					use App\Models\EnvironmentVariable;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Visus\Cuid2\Cuid2;
 | 
					use Visus\Cuid2\Cuid2;
 | 
				
			||||||
use Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class All extends Component
 | 
					class All extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,7 +88,9 @@ class Form extends Component
 | 
				
			|||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->validate();
 | 
					        $this->validate();
 | 
				
			||||||
        $uniqueIPs = Server::all()->pluck('ip')->toArray();
 | 
					        $uniqueIPs = Server::all()->reject(function (Server $server) {
 | 
				
			||||||
 | 
					            return $server->id === $this->server->id;
 | 
				
			||||||
 | 
					        })->pluck('ip')->toArray();
 | 
				
			||||||
        if (in_array($this->server->ip, $uniqueIPs)) {
 | 
					        if (in_array($this->server->ip, $uniqueIPs)) {
 | 
				
			||||||
            $this->emit('error', 'IP address is already in use by another team.');
 | 
					            $this->emit('error', 'IP address is already in use by another team.');
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ class Proxy extends Component
 | 
				
			|||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            resolve(SaveConfigurationSync::class)($this->server, $this->proxy_settings);
 | 
					            resolve(SaveConfigurationSync::class)($this->server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->server->proxy->redirect_url = $this->redirect_url;
 | 
					            $this->server->proxy->redirect_url = $this->redirect_url;
 | 
				
			||||||
            $this->server->save();
 | 
					            $this->server->save();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Http\Livewire\Server\Proxy;
 | 
					namespace App\Http\Livewire\Server\Proxy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Proxy\SaveConfigurationSync;
 | 
				
			||||||
use App\Actions\Proxy\StartProxy;
 | 
					use App\Actions\Proxy\StartProxy;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
@@ -21,7 +22,7 @@ class Deploy extends Component
 | 
				
			|||||||
            $this->server->proxy->last_applied_settings &&
 | 
					            $this->server->proxy->last_applied_settings &&
 | 
				
			||||||
            $this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings
 | 
					            $this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            resolve(SaveConfigurationSync::class)($this->server, $this->proxy_settings);
 | 
					            resolve(SaveConfigurationSync::class)($this->server);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $activity = resolve(StartProxy::class)($this->server);
 | 
					        $activity = resolve(StartProxy::class)($this->server);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								app/Http/Livewire/Server/Proxy/Modal.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/Http/Livewire/Server/Proxy/Modal.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http\Livewire\Server\Proxy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Modal extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public Server $server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function proxyStatusUpdated()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->emit('proxyStatusUpdated');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -13,7 +13,10 @@ class Show extends Component
 | 
				
			|||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->firstOrFail();
 | 
					            $this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first();
 | 
				
			||||||
 | 
					            if (is_null($this->server)) {
 | 
				
			||||||
 | 
					                return redirect()->route('server.all');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return general_error_handler(err: $e, that: $this);
 | 
					            return general_error_handler(err: $e, that: $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ class PricingPlans extends Component
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $payload = [
 | 
					        $payload = [
 | 
				
			||||||
 | 
					            'billing_address_collection' => 'required',
 | 
				
			||||||
            'client_reference_id' => auth()->user()->id . ':' . currentTeam()->id,
 | 
					            'client_reference_id' => auth()->user()->id . ':' . currentTeam()->id,
 | 
				
			||||||
            'line_items' => [[
 | 
					            'line_items' => [[
 | 
				
			||||||
                'price' => $priceId,
 | 
					                'price' => $priceId,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,6 @@ class Delete extends Component
 | 
				
			|||||||
        $currentTeam = currentTeam();
 | 
					        $currentTeam = currentTeam();
 | 
				
			||||||
        $currentTeam->delete();
 | 
					        $currentTeam->delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $team = auth()->user()->teams()->first();
 | 
					 | 
				
			||||||
        $currentTeam->members->each(function ($user) use ($currentTeam) {
 | 
					        $currentTeam->members->each(function ($user) use ($currentTeam) {
 | 
				
			||||||
            if ($user->id === auth()->user()->id) {
 | 
					            if ($user->id === auth()->user()->id) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,6 @@ class Form extends Component
 | 
				
			|||||||
        $this->validate();
 | 
					        $this->validate();
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $this->team->save();
 | 
					            $this->team->save();
 | 
				
			||||||
            refreshSession();
 | 
					 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return general_error_handler($e, $this);
 | 
					            return general_error_handler($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,9 +4,13 @@ namespace App\Http\Livewire\Team;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Models\TeamInvitation;
 | 
					use App\Models\TeamInvitation;
 | 
				
			||||||
use App\Models\User;
 | 
					use App\Models\User;
 | 
				
			||||||
use App\Notifications\TransactionalEmails\InvitationLink;
 | 
					use Illuminate\Notifications\Messages\MailMessage;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Artisan;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Visus\Cuid2\Cuid2;
 | 
					use Visus\Cuid2\Cuid2;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Crypt;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Hash;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class InviteLink extends Component
 | 
					class InviteLink extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -20,53 +24,68 @@ class InviteLink extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function viaEmail()
 | 
					    public function viaEmail()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->generate_invite_link(isEmail: true);
 | 
					        $this->generate_invite_link(sendEmail: true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function generate_invite_link(bool $isEmail = false)
 | 
					    public function viaLink()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->generate_invite_link(sendEmail: false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private function generate_invite_link(bool $sendEmail = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $uuid = new Cuid2(32);
 | 
					 | 
				
			||||||
            $link = url('/') . config('constants.invitation.link.base_url') . $uuid;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $user = User::whereEmail($this->email);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!$user->exists()) {
 | 
					 | 
				
			||||||
                return general_error_handler(that: $this, customErrorMessage: "$this->email must be registered first (or activate transactional emails to invite via email).");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $member_emails = currentTeam()->members()->get()->pluck('email');
 | 
					            $member_emails = currentTeam()->members()->get()->pluck('email');
 | 
				
			||||||
            if ($member_emails->contains($this->email)) {
 | 
					            if ($member_emails->contains($this->email)) {
 | 
				
			||||||
                return general_error_handler(that: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . ".");
 | 
					                return general_error_handler(that: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . ".");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            $uuid = new Cuid2(32);
 | 
				
			||||||
 | 
					            $link = url('/') . config('constants.invitation.link.base_url') . $uuid;
 | 
				
			||||||
 | 
					            $user = User::whereEmail($this->email)->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $invitation = TeamInvitation::whereEmail($this->email);
 | 
					            if (is_null($user)) {
 | 
				
			||||||
 | 
					                $password = Str::password();
 | 
				
			||||||
            if ($invitation->exists()) {
 | 
					                $user = User::create([
 | 
				
			||||||
                $created_at = $invitation->first()->created_at;
 | 
					                    'name' => Str::of($this->email)->before('@'),
 | 
				
			||||||
                $diff = $created_at->diffInMinutes(now());
 | 
					                    'email' => $this->email,
 | 
				
			||||||
                if ($diff <= config('constants.invitation.link.expiration')) {
 | 
					                    'password' => Hash::make($password),
 | 
				
			||||||
                    return general_error_handler(that: $this, customErrorMessage: "Invitation already sent to $this->email and waiting for action.");
 | 
					                    'force_password_reset' => true,
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					                $token = Crypt::encryptString("{$user->email}@@@$password");
 | 
				
			||||||
 | 
					                $link = route('auth.link', ['token' => $token]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $invitation = TeamInvitation::whereEmail($this->email)->first();
 | 
				
			||||||
 | 
					            if (!is_null($invitation)) {
 | 
				
			||||||
 | 
					                $invitationValid = $invitation->isValid();
 | 
				
			||||||
 | 
					                if ($invitationValid) {
 | 
				
			||||||
 | 
					                    return general_error_handler(that: $this, customErrorMessage: "Pending invitation already exists for $this->email.");
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    $invitation->delete();
 | 
					                    $invitation->delete();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            TeamInvitation::firstOrCreate([
 | 
					            $invitation = TeamInvitation::firstOrCreate([
 | 
				
			||||||
                'team_id' => currentTeam()->id,
 | 
					                'team_id' => currentTeam()->id,
 | 
				
			||||||
                'uuid' => $uuid,
 | 
					                'uuid' => $uuid,
 | 
				
			||||||
                'email' => $this->email,
 | 
					                'email' => $this->email,
 | 
				
			||||||
                'role' => $this->role,
 | 
					                'role' => $this->role,
 | 
				
			||||||
                'link' => $link,
 | 
					                'link' => $link,
 | 
				
			||||||
                'via' => $isEmail ? 'email' : 'link',
 | 
					                'via' => $sendEmail ? 'email' : 'link',
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
            if ($isEmail) {
 | 
					            if ($sendEmail) {
 | 
				
			||||||
                $user->first()->notify(new InvitationLink);
 | 
					                $mail = new MailMessage();
 | 
				
			||||||
 | 
					                $mail->view('emails.invitation-link', [
 | 
				
			||||||
 | 
					                    'team' => currentTeam()->name,
 | 
				
			||||||
 | 
					                    'invitation_link' => $link,
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					                $mail->subject('You have been invited to ' . currentTeam()->name . ' on ' . config('app.name') . '.');
 | 
				
			||||||
 | 
					                send_user_an_email($mail, $this->email);
 | 
				
			||||||
                $this->emit('success', 'Invitation sent via email successfully.');
 | 
					                $this->emit('success', 'Invitation sent via email successfully.');
 | 
				
			||||||
 | 
					                $this->emit('refreshInvitations');
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                $this->emit('success', 'Invitation link generated.');
 | 
					                $this->emit('success', 'Invitation link generated.');
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
                $this->emit('refreshInvitations');
 | 
					                $this->emit('refreshInvitations');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            $error_message = $e->getMessage();
 | 
					            $error_message = $e->getMessage();
 | 
				
			||||||
            if ($e->getCode() === '23505') {
 | 
					            if ($e->getCode() === '23505') {
 | 
				
			||||||
@@ -75,9 +94,4 @@ class InviteLink extends Component
 | 
				
			|||||||
            return general_error_handler(err: $e, that: $this, customErrorMessage: $error_message);
 | 
					            return general_error_handler(err: $e, that: $this, customErrorMessage: $error_message);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function viaLink()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->generate_invite_link();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
namespace App\Http\Livewire\Team;
 | 
					namespace App\Http\Livewire\Team;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Models\User;
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Cache;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Member extends Component
 | 
					class Member extends Component
 | 
				
			||||||
@@ -24,6 +25,10 @@ class Member extends Component
 | 
				
			|||||||
    public function remove()
 | 
					    public function remove()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->member->teams()->detach(currentTeam());
 | 
					        $this->member->teams()->detach(currentTeam());
 | 
				
			||||||
 | 
					        Cache::forget("team:{$this->member->id}");
 | 
				
			||||||
 | 
					        Cache::remember('team:' . $this->member->id, 3600, function() {
 | 
				
			||||||
 | 
					            return $this->member->teams()->first();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        $this->emit('reloadWindow');
 | 
					        $this->emit('reloadWindow');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ use App\Jobs\SendConfirmationForWaitlistJob;
 | 
				
			|||||||
use App\Models\User;
 | 
					use App\Models\User;
 | 
				
			||||||
use App\Models\Waitlist;
 | 
					use App\Models\Waitlist;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Index extends Component
 | 
					class Index extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ namespace App\Http\Middleware;
 | 
				
			|||||||
use Closure;
 | 
					use Closure;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
use Symfony\Component\HttpFoundation\Response;
 | 
					use Symfony\Component\HttpFoundation\Response;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IsBoardingFlow
 | 
					class IsBoardingFlow
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -17,6 +18,9 @@ class IsBoardingFlow
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        // ray()->showQueries()->color('orange');
 | 
					        // ray()->showQueries()->color('orange');
 | 
				
			||||||
        if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
 | 
					        if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
 | 
				
			||||||
 | 
					            if (Str::startsWith($request->path(), 'invitations')) {
 | 
				
			||||||
 | 
					                return $next($request);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            return redirect('boarding');
 | 
					            return redirect('boarding');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $next($request);
 | 
					        return $next($request);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ namespace App\Http\Middleware;
 | 
				
			|||||||
use Closure;
 | 
					use Closure;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
use Symfony\Component\HttpFoundation\Response;
 | 
					use Symfony\Component\HttpFoundation\Response;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IsSubscriptionValid
 | 
					class IsSubscriptionValid
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -31,6 +32,9 @@ class IsSubscriptionValid
 | 
				
			|||||||
        if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
 | 
					        if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
 | 
				
			||||||
            // ray('SubscriptionValid Middleware');
 | 
					            // ray('SubscriptionValid Middleware');
 | 
				
			||||||
            if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
 | 
					            if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
 | 
				
			||||||
 | 
					                if (Str::startsWith($request->path(), 'invitations')) {
 | 
				
			||||||
 | 
					                    return $next($request);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                return redirect('subscription');
 | 
					                return redirect('subscription');
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                return $next($request);
 | 
					                return $next($request);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,54 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace App\Jobs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use App\Models\Application;
 | 
					 | 
				
			||||||
use App\Models\ApplicationPreview;
 | 
					 | 
				
			||||||
use Illuminate\Bus\Queueable;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
					 | 
				
			||||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
					 | 
				
			||||||
use Illuminate\Queue\InteractsWithQueue;
 | 
					 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ApplicationContainerStatusJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public string $containerName;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function __construct(
 | 
					 | 
				
			||||||
        public Application $application,
 | 
					 | 
				
			||||||
        public int $pullRequestId = 0)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->containerName = generateApplicationContainerName($application->uuid, $pullRequestId);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function uniqueId(): string
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return $this->containerName;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function handle(): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            $status = getApplicationContainerStatus(application: $this->application);
 | 
					 | 
				
			||||||
            if ($this->application->status === 'running' && $status !== 'running') {
 | 
					 | 
				
			||||||
                // $this->application->environment->project->team->notify(new StatusChanged($this->application));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if ($this->pullRequestId !== 0) {
 | 
					 | 
				
			||||||
                $preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pullRequestId);
 | 
					 | 
				
			||||||
                $preview->status = $status;
 | 
					 | 
				
			||||||
                $preview->save();
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                $this->application->status = $status;
 | 
					 | 
				
			||||||
                $this->application->save();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					 | 
				
			||||||
            ray($e->getMessage());
 | 
					 | 
				
			||||||
            throw $e;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -652,7 +652,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private function generate_healthcheck_commands()
 | 
					    private function generate_healthcheck_commands()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->application->dockerfile) {
 | 
					        if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile') {
 | 
				
			||||||
            // TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl.
 | 
					            // TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl.
 | 
				
			||||||
            return 'exit 0';
 | 
					            return 'exit 0';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Jobs;
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\TeamInvitation;
 | 
				
			||||||
use App\Models\Waitlist;
 | 
					use App\Models\Waitlist;
 | 
				
			||||||
use Illuminate\Bus\Queueable;
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
					use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
				
			||||||
@@ -32,7 +33,12 @@ class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique, ShouldBeE
 | 
				
			|||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
 | 
					            send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
 | 
				
			||||||
            ray($e->getMessage());
 | 
					            ray($e->getMessage());
 | 
				
			||||||
            throw $e;
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $this->cleanup_invitation_link();
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
 | 
				
			||||||
 | 
					            ray($e->getMessage());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,4 +49,11 @@ class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique, ShouldBeE
 | 
				
			|||||||
            $item->delete();
 | 
					            $item->delete();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    private function cleanup_invitation_link()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $invitation = TeamInvitation::all();
 | 
				
			||||||
 | 
					        foreach ($invitation as $item) {
 | 
				
			||||||
 | 
					            $item->isValid();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,9 +17,9 @@ use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			|||||||
use Illuminate\Queue\InteractsWithQueue;
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
					use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
use Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ContainerStatusJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
 | 
					class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -89,7 +89,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypt
 | 
				
			|||||||
                $labels = data_get($container, 'Config.Labels');
 | 
					                $labels = data_get($container, 'Config.Labels');
 | 
				
			||||||
                $labels = Arr::undot(format_docker_labels_to_json($labels));
 | 
					                $labels = Arr::undot(format_docker_labels_to_json($labels));
 | 
				
			||||||
                $labelId = data_get($labels, 'coolify.applicationId');
 | 
					                $labelId = data_get($labels, 'coolify.applicationId');
 | 
				
			||||||
                ray($labelId);
 | 
					 | 
				
			||||||
                if ($labelId) {
 | 
					                if ($labelId) {
 | 
				
			||||||
                    if (str_contains($labelId,'-pr-')) {
 | 
					                    if (str_contains($labelId,'-pr-')) {
 | 
				
			||||||
                        $previewId = (int) Str::after($labelId, '-pr-');
 | 
					                        $previewId = (int) Str::after($labelId, '-pr-');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,57 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace App\Jobs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use App\Models\ApplicationPreview;
 | 
					 | 
				
			||||||
use App\Models\StandalonePostgresql;
 | 
					 | 
				
			||||||
use App\Notifications\Application\StatusChanged;
 | 
					 | 
				
			||||||
use Illuminate\Bus\Queueable;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
					 | 
				
			||||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
					 | 
				
			||||||
use Illuminate\Queue\InteractsWithQueue;
 | 
					 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class DatabaseContainerStatusJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public string $containerName;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function __construct(
 | 
					 | 
				
			||||||
        public StandalonePostgresql $database,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        $this->containerName = $database->uuid;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function uniqueId(): string
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return $this->containerName;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function handle(): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            $status = getContainerStatus(
 | 
					 | 
				
			||||||
                server: $this->database->destination->server,
 | 
					 | 
				
			||||||
                container_id: $this->containerName,
 | 
					 | 
				
			||||||
                throwError: false
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if ($this->database->status === 'running' && $status !== 'running') {
 | 
					 | 
				
			||||||
                if (data_get($this->database, 'environment.project.team')) {
 | 
					 | 
				
			||||||
                    // $this->database->environment->project->team->notify(new StatusChanged($this->database));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if ($this->database->status !== $status) {
 | 
					 | 
				
			||||||
                $this->database->status = $status;
 | 
					 | 
				
			||||||
                $this->database->save();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					 | 
				
			||||||
            send_internal_notification('DatabaseContainerStatusJob failed with: ' . $e->getMessage());
 | 
					 | 
				
			||||||
            ray($e->getMessage());
 | 
					 | 
				
			||||||
            throw $e;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,87 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace App\Jobs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use App\Actions\Proxy\StartProxy;
 | 
					 | 
				
			||||||
use App\Enums\ProxyStatus;
 | 
					 | 
				
			||||||
use App\Enums\ProxyTypes;
 | 
					 | 
				
			||||||
use App\Models\Server;
 | 
					 | 
				
			||||||
use Illuminate\Bus\Queueable;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
 | 
					 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
					 | 
				
			||||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
					 | 
				
			||||||
use Illuminate\Queue\InteractsWithQueue;
 | 
					 | 
				
			||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
					 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ProxyContainerStatusJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public Server $server;
 | 
					 | 
				
			||||||
    public $tries = 1;
 | 
					 | 
				
			||||||
    public $timeout = 120;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function __construct(Server $server)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->server = $server;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function middleware(): array
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return [new WithoutOverlapping($this->server->uuid)];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function uniqueId(): string
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        ray($this->server->uuid);
 | 
					 | 
				
			||||||
        return $this->server->uuid;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function handle(): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            $proxyType = data_get($this->server, 'proxy.type');
 | 
					 | 
				
			||||||
            if ($proxyType === ProxyTypes::NONE->value) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (is_null($proxyType)) {
 | 
					 | 
				
			||||||
                if ($this->server->isProxyShouldRun()) {
 | 
					 | 
				
			||||||
                    $this->server->proxy->type = ProxyTypes::TRAEFIK_V2->value;
 | 
					 | 
				
			||||||
                    $this->server->proxy->status = ProxyStatus::EXITED->value;
 | 
					 | 
				
			||||||
                    $this->server->save();
 | 
					 | 
				
			||||||
                    resolve(StartProxy::class)($this->server);
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $container = getContainerStatus(server: $this->server, all_data: true, container_id: 'coolify-proxy', throwError: false);
 | 
					 | 
				
			||||||
            $containerStatus = data_get($container, 'State.Status');
 | 
					 | 
				
			||||||
            $databaseContainerStatus = data_get($this->server, 'proxy.status', 'exited');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if ($proxyType !== ProxyTypes::NONE->value) {
 | 
					 | 
				
			||||||
                if ($containerStatus === 'running') {
 | 
					 | 
				
			||||||
                    $this->server->proxy->status = $containerStatus;
 | 
					 | 
				
			||||||
                    $this->server->save();
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if ((is_null($containerStatus) ||$containerStatus !== 'running' || $databaseContainerStatus !== 'running' || ($containerStatus && $databaseContainerStatus !== $containerStatus)) && $this->server->isProxyShouldRun()) {
 | 
					 | 
				
			||||||
                    $this->server->proxy->status = $containerStatus;
 | 
					 | 
				
			||||||
                    $this->server->save();
 | 
					 | 
				
			||||||
                    resolve(StartProxy::class)($this->server);
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					 | 
				
			||||||
            if ($e->getCode() === 1) {
 | 
					 | 
				
			||||||
                $this->server->proxy->status = 'exited';
 | 
					 | 
				
			||||||
                $this->server->save();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            send_internal_notification('ProxyContainerStatusJob failed with: ' . $e->getMessage());
 | 
					 | 
				
			||||||
            ray($e->getMessage());
 | 
					 | 
				
			||||||
            throw $e;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -9,7 +9,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			|||||||
use Illuminate\Queue\InteractsWithQueue;
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
use Illuminate\Support\Facades\Http;
 | 
					use Illuminate\Support\Facades\Http;
 | 
				
			||||||
use Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SendMessageToTelegramJob implements ShouldQueue, ShouldBeEncrypted
 | 
					class SendMessageToTelegramJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,13 @@ class Team extends Model implements SendsDiscord, SendsEmail
 | 
				
			|||||||
        'resend_api_key' => 'encrypted',
 | 
					        'resend_api_key' => 'encrypted',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static function booted()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static::saved(function () {
 | 
				
			||||||
 | 
					            refreshSession();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function routeNotificationForDiscord()
 | 
					    public function routeNotificationForDiscord()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return data_get($this, 'discord_webhook_url', null);
 | 
					        return data_get($this, 'discord_webhook_url', null);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,4 +19,13 @@ class TeamInvitation extends Model
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->belongsTo(Team::class);
 | 
					        return $this->belongsTo(Team::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function isValid() {
 | 
				
			||||||
 | 
					        $createdAt = $this->created_at;
 | 
				
			||||||
 | 
					        $diff = $createdAt->diffInMinutes(now());
 | 
				
			||||||
 | 
					        if ($diff <= config('constants.invitation.link.expiration')) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->delete();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,10 @@ namespace App\Models;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Notifications\Channels\SendsEmail;
 | 
					use App\Notifications\Channels\SendsEmail;
 | 
				
			||||||
use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword;
 | 
					use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword;
 | 
				
			||||||
use Cache;
 | 
					 | 
				
			||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
 | 
					use Illuminate\Foundation\Auth\User as Authenticatable;
 | 
				
			||||||
use Illuminate\Notifications\Notifiable;
 | 
					use Illuminate\Notifications\Notifiable;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Cache;
 | 
				
			||||||
use Laravel\Fortify\TwoFactorAuthenticatable;
 | 
					use Laravel\Fortify\TwoFactorAuthenticatable;
 | 
				
			||||||
use Laravel\Sanctum\HasApiTokens;
 | 
					use Laravel\Sanctum\HasApiTokens;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,7 +61,7 @@ class User extends Authenticatable implements SendsEmail
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function isAdmin()
 | 
					    public function isAdmin()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->pivot->role === 'admin' || $this->pivot->role === 'owner';
 | 
					        return data_get($this->pivot,'role') === 'admin' || data_get($this->pivot,'role') === 'owner';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function isAdminFromSession()
 | 
					    public function isAdminFromSession()
 | 
				
			||||||
@@ -78,7 +78,8 @@ class User extends Authenticatable implements SendsEmail
 | 
				
			|||||||
        if ($is_part_of_root_team && $is_admin_of_root_team) {
 | 
					        if ($is_part_of_root_team && $is_admin_of_root_team) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $role = $teams->where('id', auth()->user()->id)->first()->pivot->role;
 | 
					        $team = $teams->where('id', session('currentTeam')->id)->first();
 | 
				
			||||||
 | 
					        $role = data_get($team,'pivot.role');
 | 
				
			||||||
        return $role === 'admin' || $role === 'owner';
 | 
					        return $role === 'admin' || $role === 'owner';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ trait ExecuteRemoteCommand
 | 
				
			|||||||
            $this->save = data_get($single_command, 'save');
 | 
					            $this->save = data_get($single_command, 'save');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $remote_command = generateSshCommand( $ip, $user, $port, $command);
 | 
					            $remote_command = generateSshCommand( $ip, $user, $port, $command);
 | 
				
			||||||
            $process =  processWithEnv()->timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden) {
 | 
					            $process =  Process::timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden) {
 | 
				
			||||||
                $output = Str::of($output)->trim();
 | 
					                $output = Str::of($output)->trim();
 | 
				
			||||||
                $new_log_entry = [
 | 
					                $new_log_entry = [
 | 
				
			||||||
                    'command' => $command,
 | 
					                    'command' => $command,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ class Textarea extends Component
 | 
				
			|||||||
        public bool        $disabled = false,
 | 
					        public bool        $disabled = false,
 | 
				
			||||||
        public bool        $readonly = false,
 | 
					        public bool        $readonly = false,
 | 
				
			||||||
        public string|null $helper = null,
 | 
					        public string|null $helper = null,
 | 
				
			||||||
 | 
					        public bool        $realtimeValidation = false,
 | 
				
			||||||
        public string      $defaultClass = "textarea bg-coolgray-200 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
 | 
					        public string      $defaultClass = "textarea bg-coolgray-200 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,7 +75,6 @@ function getApplicationContainerStatus(Application $application) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
function getContainerStatus(Server $server, string $container_id, bool $all_data = false, bool $throwError = false)
 | 
					function getContainerStatus(Server $server, string $container_id, bool $all_data = false, bool $throwError = false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // check_server_connection($server);
 | 
					 | 
				
			||||||
    $container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError);
 | 
					    $container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError);
 | 
				
			||||||
    if (!$container) {
 | 
					    if (!$container) {
 | 
				
			||||||
        return 'exited';
 | 
					        return 'exited';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ use Illuminate\Support\Facades\Process;
 | 
				
			|||||||
use Illuminate\Support\Facades\Storage;
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
use Illuminate\Support\Sleep;
 | 
					use Illuminate\Support\Sleep;
 | 
				
			||||||
use Spatie\Activitylog\Models\Activity;
 | 
					use Spatie\Activitylog\Models\Activity;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function remote_process(
 | 
					function remote_process(
 | 
				
			||||||
    array   $command,
 | 
					    array   $command,
 | 
				
			||||||
@@ -49,20 +50,23 @@ function remote_process(
 | 
				
			|||||||
    ])();
 | 
					    ])();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function removePrivateKeyFromSshAgent(Server $server)
 | 
					// function removePrivateKeyFromSshAgent(Server $server)
 | 
				
			||||||
{
 | 
					// {
 | 
				
			||||||
    if (data_get($server, 'privateKey.private_key') === null) {
 | 
					//     if (data_get($server, 'privateKey.private_key') === null) {
 | 
				
			||||||
        throw new \Exception("Server {$server->name} does not have a private key");
 | 
					//         throw new \Exception("Server {$server->name} does not have a private key");
 | 
				
			||||||
    }
 | 
					//     }
 | 
				
			||||||
    processWithEnv()->run("echo '{$server->privateKey->private_key}' | ssh-add -d -");
 | 
					//     // processWithEnv()->run("echo '{$server->privateKey->private_key}' | ssh-add -d -");
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
function addPrivateKeyToSshAgent(Server $server)
 | 
					function addPrivateKeyToSshAgent(Server $server)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (data_get($server, 'privateKey.private_key') === null) {
 | 
					    if (data_get($server, 'privateKey.private_key') === null) {
 | 
				
			||||||
        throw new \Exception("Server {$server->name} does not have a private key");
 | 
					        throw new \Exception("Server {$server->name} does not have a private key");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // ray('adding key', $server->privateKey->private_key);
 | 
					    $sshKeyFileLocation = "id.root@{$server->uuid}";
 | 
				
			||||||
    processWithEnv()->run("echo '{$server->privateKey->private_key}' | ssh-add -q -");
 | 
					    Storage::disk('ssh-keys')->makeDirectory('.');
 | 
				
			||||||
 | 
					    Storage::disk('ssh-mux')->makeDirectory('.');
 | 
				
			||||||
 | 
					    Storage::disk('ssh-keys')->put($sshKeyFileLocation, $server->privateKey->private_key);
 | 
				
			||||||
 | 
					    return '/var/www/html/storage/app/ssh/keys/' . $sshKeyFileLocation;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function generateSshCommand(string $server_ip, string $user, string $port, string $command, bool $isMux = true)
 | 
					function generateSshCommand(string $server_ip, string $user, string $port, string $command, bool $isMux = true)
 | 
				
			||||||
@@ -71,7 +75,7 @@ function generateSshCommand(string $server_ip, string $user, string $port, strin
 | 
				
			|||||||
    if (!$server) {
 | 
					    if (!$server) {
 | 
				
			||||||
        throw new \Exception("Server with ip {$server_ip} not found");
 | 
					        throw new \Exception("Server with ip {$server_ip} not found");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    addPrivateKeyToSshAgent($server);
 | 
					    $privateKeyLocation = addPrivateKeyToSshAgent($server);
 | 
				
			||||||
    $timeout = config('constants.ssh.command_timeout');
 | 
					    $timeout = config('constants.ssh.command_timeout');
 | 
				
			||||||
    $connectionTimeout = config('constants.ssh.connection_timeout');
 | 
					    $connectionTimeout = config('constants.ssh.connection_timeout');
 | 
				
			||||||
    $serverInterval = config('constants.ssh.server_interval');
 | 
					    $serverInterval = config('constants.ssh.server_interval');
 | 
				
			||||||
@@ -83,7 +87,8 @@ function generateSshCommand(string $server_ip, string $user, string $port, strin
 | 
				
			|||||||
        $ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ';
 | 
					        $ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    $command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
 | 
					    $command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
 | 
				
			||||||
    $ssh_command .= '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
 | 
					    $ssh_command .= "-i {$privateKeyLocation} "
 | 
				
			||||||
 | 
					        . '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
 | 
				
			||||||
        . '-o PasswordAuthentication=no '
 | 
					        . '-o PasswordAuthentication=no '
 | 
				
			||||||
        . "-o ConnectTimeout=$connectionTimeout "
 | 
					        . "-o ConnectTimeout=$connectionTimeout "
 | 
				
			||||||
        . "-o ServerAliveInterval=$serverInterval "
 | 
					        . "-o ServerAliveInterval=$serverInterval "
 | 
				
			||||||
@@ -97,28 +102,11 @@ function generateSshCommand(string $server_ip, string $user, string $port, strin
 | 
				
			|||||||
    // ray($ssh_command);
 | 
					    // ray($ssh_command);
 | 
				
			||||||
    return $ssh_command;
 | 
					    return $ssh_command;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function processWithEnv()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    return Process::env(['SSH_AUTH_SOCK' => config('coolify.ssh_auth_sock')]);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function instantCommand(string $command, $throwError = true)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    $process = processWithEnv()->run($command);
 | 
					 | 
				
			||||||
    $output = trim($process->output());
 | 
					 | 
				
			||||||
    $exitCode = $process->exitCode();
 | 
					 | 
				
			||||||
    if ($exitCode !== 0) {
 | 
					 | 
				
			||||||
        if (!$throwError) {
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        throw new \RuntimeException($process->errorOutput(), $exitCode);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return $output;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function instant_remote_process(array $command, Server $server, $throwError = true, $repeat = 1)
 | 
					function instant_remote_process(array $command, Server $server, $throwError = true, $repeat = 1)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $command_string = implode("\n", $command);
 | 
					    $command_string = implode("\n", $command);
 | 
				
			||||||
    $ssh_command = generateSshCommand($server->ip, $server->user, $server->port, $command_string);
 | 
					    $ssh_command = generateSshCommand($server->ip, $server->user, $server->port, $command_string);
 | 
				
			||||||
    $process =  processWithEnv()->run($ssh_command);
 | 
					    $process = Process::run($ssh_command);
 | 
				
			||||||
    $output = trim($process->output());
 | 
					    $output = trim($process->output());
 | 
				
			||||||
    $exitCode = $process->exitCode();
 | 
					    $exitCode = $process->exitCode();
 | 
				
			||||||
    if ($exitCode !== 0) {
 | 
					    if ($exitCode !== 0) {
 | 
				
			||||||
@@ -169,14 +157,8 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
 | 
				
			|||||||
function refresh_server_connection(PrivateKey $private_key)
 | 
					function refresh_server_connection(PrivateKey $private_key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    foreach ($private_key->servers as $server) {
 | 
					    foreach ($private_key->servers as $server) {
 | 
				
			||||||
        // Delete the old ssh mux file to force a new one to be created
 | 
					 | 
				
			||||||
        Storage::disk('ssh-mux')->delete($server->muxFilename());
 | 
					        Storage::disk('ssh-mux')->delete($server->muxFilename());
 | 
				
			||||||
        // check if user is authenticated
 | 
					 | 
				
			||||||
        // if (currentTeam()->id) {
 | 
					 | 
				
			||||||
        //     currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get();
 | 
					 | 
				
			||||||
        // }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    removePrivateKeyFromSshAgent($server);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function validateServer(Server $server)
 | 
					function validateServer(Server $server)
 | 
				
			||||||
@@ -221,29 +203,6 @@ function validateServer(Server $server)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function check_server_connection(Server $server)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
        refresh_server_connection($server->privateKey);
 | 
					 | 
				
			||||||
        instant_remote_process(['uptime'], $server);
 | 
					 | 
				
			||||||
        $server->unreachable_count = 0;
 | 
					 | 
				
			||||||
        $server->settings->is_reachable = true;
 | 
					 | 
				
			||||||
    } catch (\Throwable $e) {
 | 
					 | 
				
			||||||
        if ($server->unreachable_count == 2) {
 | 
					 | 
				
			||||||
            $server->team->notify(new NotReachable($server));
 | 
					 | 
				
			||||||
            $server->settings->is_reachable = false;
 | 
					 | 
				
			||||||
            $server->settings->save();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            $server->unreachable_count += 1;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        throw $e;
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
        $server->settings->save();
 | 
					 | 
				
			||||||
        $server->save();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function checkRequiredCommands(Server $server)
 | 
					function checkRequiredCommands(Server $server)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $commands = collect(["jq", "jc"]);
 | 
					    $commands = collect(["jq", "jc"]);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Models\InstanceSettings;
 | 
					use App\Models\InstanceSettings;
 | 
				
			||||||
use App\Models\Team;
 | 
					use App\Models\Team;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
use App\Notifications\Channels\DiscordChannel;
 | 
					use App\Notifications\Channels\DiscordChannel;
 | 
				
			||||||
use App\Notifications\Channels\EmailChannel;
 | 
					use App\Notifications\Channels\EmailChannel;
 | 
				
			||||||
use App\Notifications\Channels\TelegramChannel;
 | 
					use App\Notifications\Channels\TelegramChannel;
 | 
				
			||||||
@@ -10,6 +11,7 @@ use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
 | 
				
			|||||||
use Illuminate\Database\QueryException;
 | 
					use Illuminate\Database\QueryException;
 | 
				
			||||||
use Illuminate\Mail\Message;
 | 
					use Illuminate\Mail\Message;
 | 
				
			||||||
use Illuminate\Notifications\Messages\MailMessage;
 | 
					use Illuminate\Notifications\Messages\MailMessage;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Cache;
 | 
				
			||||||
use Illuminate\Support\Facades\Http;
 | 
					use Illuminate\Support\Facades\Http;
 | 
				
			||||||
use Illuminate\Support\Facades\Mail;
 | 
					use Illuminate\Support\Facades\Mail;
 | 
				
			||||||
use Illuminate\Support\Facades\Route;
 | 
					use Illuminate\Support\Facades\Route;
 | 
				
			||||||
@@ -60,7 +62,11 @@ function showBoarding(): bool
 | 
				
			|||||||
function refreshSession(?Team $team = null): void
 | 
					function refreshSession(?Team $team = null): void
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (!$team) {
 | 
					    if (!$team) {
 | 
				
			||||||
        $team = Team::find(currentTeam()->id);
 | 
					        if (auth()->user()->currentTeam()) {
 | 
				
			||||||
 | 
					            $team = Team::find(auth()->user()->currentTeam()->id);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $team = User::find(auth()->user()->id)->teams->first();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Cache::forget('team:' . auth()->user()->id);
 | 
					    Cache::forget('team:' . auth()->user()->id);
 | 
				
			||||||
    Cache::remember('team:' . auth()->user()->id, 3600, function() use ($team) {
 | 
					    Cache::remember('team:' . auth()->user()->id, 3600, function() use ($team) {
 | 
				
			||||||
@@ -275,6 +281,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null
 | 
				
			|||||||
            [],
 | 
					            [],
 | 
				
			||||||
            fn (Message $message) => $message
 | 
					            fn (Message $message) => $message
 | 
				
			||||||
                ->to($email)
 | 
					                ->to($email)
 | 
				
			||||||
 | 
					                ->replyTo($email)
 | 
				
			||||||
                ->cc($cc)
 | 
					                ->cc($cc)
 | 
				
			||||||
                ->subject($mail->subject)
 | 
					                ->subject($mail->subject)
 | 
				
			||||||
                ->html((string) $mail->render())
 | 
					                ->html((string) $mail->render())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,7 +56,7 @@ function isSubscriptionActive()
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    $subscription = $team?->subscription;
 | 
					    $subscription = $team?->subscription;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!$subscription) {
 | 
					    if (is_null($subscription)) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (isLemon()) {
 | 
					    if (isLemon()) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,5 +8,4 @@ return [
 | 
				
			|||||||
    'dev_webhook' => env('SERVEO_URL'),
 | 
					    'dev_webhook' => env('SERVEO_URL'),
 | 
				
			||||||
    'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
 | 
					    'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
 | 
				
			||||||
    'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper:latest'),
 | 
					    'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper:latest'),
 | 
				
			||||||
    'ssh_auth_sock' => env('SSH_AUTH_SOCK', '/tmp/coolify-ssh-agent.sock'),
 | 
					 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ return [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // The release version of your application
 | 
					    // The release version of your application
 | 
				
			||||||
    // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
 | 
					    // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
 | 
				
			||||||
    'release' => '4.0.0-beta.36',
 | 
					    'release' => '4.0.0-beta.37',
 | 
				
			||||||
    'server_name' => env('APP_ID', 'coolify'),
 | 
					    'server_name' => env('APP_ID', 'coolify'),
 | 
				
			||||||
    // When left empty or `null` the Laravel environment will be used
 | 
					    // When left empty or `null` the Laravel environment will be used
 | 
				
			||||||
    'environment' => config('app.env'),
 | 
					    'environment' => config('app.env'),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return '4.0.0-beta.36';
 | 
					return '4.0.0-beta.37';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<?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('team_invitations', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->text('link')->change();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('team_invitations', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('link')->change();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -21,7 +21,6 @@ services:
 | 
				
			|||||||
      SSL_MODE: "off"
 | 
					      SSL_MODE: "off"
 | 
				
			||||||
      AUTORUN_LARAVEL_STORAGE_LINK: "false"
 | 
					      AUTORUN_LARAVEL_STORAGE_LINK: "false"
 | 
				
			||||||
      AUTORUN_LARAVEL_MIGRATION: "false"
 | 
					      AUTORUN_LARAVEL_MIGRATION: "false"
 | 
				
			||||||
      SSH_AUTH_SOCK: "/tmp/coolify-ssh-agent.sock"
 | 
					 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - .:/var/www/html/:cached
 | 
					      - .:/var/www/html/:cached
 | 
				
			||||||
  postgres:
 | 
					  postgres:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,7 +64,6 @@ services:
 | 
				
			|||||||
      - LEMON_SQUEEZY_BASIC_PLAN_IDS
 | 
					      - LEMON_SQUEEZY_BASIC_PLAN_IDS
 | 
				
			||||||
      - LEMON_SQUEEZY_PRO_PLAN_IDS
 | 
					      - LEMON_SQUEEZY_PRO_PLAN_IDS
 | 
				
			||||||
      - LEMON_SQUEEZY_ULTIMATE_PLAN_IDS
 | 
					      - LEMON_SQUEEZY_ULTIMATE_PLAN_IDS
 | 
				
			||||||
      - SSH_AUTH_SOCK="/tmp/coolify-ssh-agent.sock"
 | 
					 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - "${APP_PORT:-8000}:80"
 | 
					      - "${APP_PORT:-8000}:80"
 | 
				
			||||||
    expose:
 | 
					    expose:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
oneshot
 | 
					 | 
				
			||||||
@@ -1,5 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/execlineb -P
 | 
					 | 
				
			||||||
foreground {
 | 
					 | 
				
			||||||
  s6-sleep 5
 | 
					 | 
				
			||||||
  su - webuser -c "ssh-agent -a /tmp/coolify-ssh-agent.sock"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
oneshot
 | 
					 | 
				
			||||||
@@ -1,5 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/execlineb -P
 | 
					 | 
				
			||||||
foreground {
 | 
					 | 
				
			||||||
  s6-sleep 5
 | 
					 | 
				
			||||||
  su - webuser -c "ssh-agent -a /tmp/coolify-ssh-agent.sock"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -30,9 +30,12 @@
 | 
				
			|||||||
        </label>
 | 
					        </label>
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
    <textarea placeholder="{{ $placeholder }}" {{ $attributes->merge(['class' => $defaultClass]) }}
 | 
					    <textarea placeholder="{{ $placeholder }}" {{ $attributes->merge(['class' => $defaultClass]) }}
 | 
				
			||||||
        wire:model.defer={{ $id }} @disabled($disabled) @readonly($readonly) @required($required)
 | 
					        @if ($realtimeValidation) wire:model.debounce.500ms="{{ $id }}"
 | 
				
			||||||
        id="{{ $id }}" name="{{ $name }}" name={{ $id }} wire:model.defer={{ $value ?? $id }}
 | 
					        @else
 | 
				
			||||||
        wire:dirty.class="input-warning"></textarea>
 | 
					        wire:model.defer={{ $value ?? $id }}
 | 
				
			||||||
 | 
					        wire:dirty.class="input-warning"@endif
 | 
				
			||||||
 | 
					        @disabled($disabled) @readonly($readonly) @required($required) id="{{ $id }}" name="{{ $name }}"
 | 
				
			||||||
 | 
					        name={{ $id }}  ></textarea>
 | 
				
			||||||
    @error($id)
 | 
					    @error($id)
 | 
				
			||||||
        <label class="label">
 | 
					        <label class="label">
 | 
				
			||||||
            <span class="text-red-500 label-text-alt">{{ $message }}</span>
 | 
					            <span class="text-red-500 label-text-alt">{{ $message }}</span>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,8 @@
 | 
				
			|||||||
                </label>
 | 
					                </label>
 | 
				
			||||||
            </fieldset>
 | 
					            </fieldset>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="py-2 text-center"><span class="font-bold text-warning">{{config('constants.limits.trial_period')}} days trial</span> included on all plans, without credit card details.</div>
 | 
					        <div class="py-2 text-center"><span class="font-bold text-warning">{{ config('constants.limits.trial_period') }}
 | 
				
			||||||
 | 
					                days trial</span> included on all plans, without credit card details.</div>
 | 
				
			||||||
        <div x-show="selected === 'monthly'" class="flex justify-center h-10 mt-3 text-sm leading-6 ">
 | 
					        <div x-show="selected === 'monthly'" class="flex justify-center h-10 mt-3 text-sm leading-6 ">
 | 
				
			||||||
            <div>Save <span class="font-bold text-warning">1 month</span> annually with the yearly plans.
 | 
					            <div>Save <span class="font-bold text-warning">1 month</span> annually with the yearly plans.
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
@@ -289,6 +290,170 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="pt-8 pb-12 text-4xl font-bold text-center text-white">Included in all plans</div>
 | 
				
			||||||
 | 
					    <div class="grid grid-cols-1 gap-10 md:grid-cols-2 gap-y-28">
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-4 mb-4">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
 | 
				
			||||||
 | 
					                    <svg width="512" height="512" class="icon" viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					                        xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					                        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
 | 
				
			||||||
 | 
					                            stroke-width="2"
 | 
				
			||||||
 | 
					                            d="M3 7a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3zm12 13H6a3 3 0 0 1-3-3v-2a3 3 0 0 1 3-3h12M7 8v.01M7 16v.01M20 15l-2 3h3l-2 3" />
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="text-2xl font-semibold text-white">Bring Your Own Servers</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="mt-1 text-base leading-7 text-gray-300">
 | 
				
			||||||
 | 
					                Bring your own server from any cloud providers, or even your own server at home! All you need is SSH
 | 
				
			||||||
 | 
					                access. You will have full control over your server, and you can even use it for other purposes.
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-4 mb-4">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
 | 
				
			||||||
 | 
					                    <svg width="512" height="512" class="icon" viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					                        xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					                        <g fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round"
 | 
				
			||||||
 | 
					                            stroke-width="2">
 | 
				
			||||||
 | 
					                            <path
 | 
				
			||||||
 | 
					                                d="M7 7h10a2 2 0 0 1 2 2v1l1 1v3l-1 1v3a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-3l-1-1v-3l1-1V9a2 2 0 0 1 2-2zm3 9h4" />
 | 
				
			||||||
 | 
					                            <circle cx="8.5" cy="11.5" r=".5" fill="#000000" />
 | 
				
			||||||
 | 
					                            <circle cx="15.5" cy="11.5" r=".5" fill="#000000" />
 | 
				
			||||||
 | 
					                            <path d="M9 7L8 3m7 4l1-4" />
 | 
				
			||||||
 | 
					                        </g>
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="text-2xl font-semibold text-white">Server Automations</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="mt-1 text-base leading-7 text-gray-300">
 | 
				
			||||||
 | 
					                Once you connected your server, Coolify will start managing it and do a
 | 
				
			||||||
 | 
					                lot of adminstrative tasks for you. You can also write your own scripts to
 | 
				
			||||||
 | 
					                automate your server<span class="text-warning">*</span>.
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-4 mb-4">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
 | 
				
			||||||
 | 
					                    <svg width="512" height="512" viewBox="0 0 24 24" class="icon"
 | 
				
			||||||
 | 
					                        xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					                        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
 | 
				
			||||||
 | 
					                            stroke-width="2">
 | 
				
			||||||
 | 
					                            <path d="M15 11h2a2 2 0 0 1 2 2v2m0 4a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2h4" />
 | 
				
			||||||
 | 
					                            <path d="M11 16a1 1 0 1 0 2 0a1 1 0 1 0-2 0m-3-5V8m.347-3.631A4 4 0 0 1 16 6M3 3l18 18" />
 | 
				
			||||||
 | 
					                        </g>
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="text-2xl font-semibold text-white">No Vendor Lock-in</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="mt-1 text-base leading-7 text-gray-300">
 | 
				
			||||||
 | 
					                You own your own data. All configurations saved on your own servers, so if
 | 
				
			||||||
 | 
					                you decide to stop using Coolify, you can still continue to manage your
 | 
				
			||||||
 | 
					                deployed resources.
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-4 mb-4">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
 | 
				
			||||||
 | 
					                    <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"
 | 
				
			||||||
 | 
					                        stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
 | 
				
			||||||
 | 
					                        <path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
				
			||||||
 | 
					                        <rect x="3" y="4" width="18" height="12" rx="1" />
 | 
				
			||||||
 | 
					                        <path d="M7 20h10" />
 | 
				
			||||||
 | 
					                        <path d="M9 16v4" />
 | 
				
			||||||
 | 
					                        <path d="M15 16v4" />
 | 
				
			||||||
 | 
					                        <path d="M7 10h2l2 3l2 -6l1 3h3" />
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="text-2xl font-semibold text-white">Monitoring</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="mt-1 text-base leading-7 text-gray-300">
 | 
				
			||||||
 | 
					                Coolify will automatically monitor your configured servers and deployed
 | 
				
			||||||
 | 
					                resources. Notifies you if something goes wrong on your favourite
 | 
				
			||||||
 | 
					                channels, like Discord, Telegram, via Email and more...
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-4 mb-4">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
 | 
				
			||||||
 | 
					                    <svg width="512" height="512" class="icon" viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					                        xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					                        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
 | 
				
			||||||
 | 
					                            stroke-width="2">
 | 
				
			||||||
 | 
					                            <path d="M6 4h10l4 4v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2" />
 | 
				
			||||||
 | 
					                            <path d="M10 14a2 2 0 1 0 4 0a2 2 0 1 0-4 0m4-10v4H8V4" />
 | 
				
			||||||
 | 
					                        </g>
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="text-2xl font-semibold text-white">Automatic Backups</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="mt-1 text-base leading-7 text-gray-300">
 | 
				
			||||||
 | 
					                We automatically backup your databases to any S3 compatible solution. If
 | 
				
			||||||
 | 
					                something goes wrong, you can easily restore your data with a few clicks.
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-4 mb-4">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
 | 
				
			||||||
 | 
					                    <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"
 | 
				
			||||||
 | 
					                        stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
 | 
				
			||||||
 | 
					                        <path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
				
			||||||
 | 
					                        <polyline points="5 7 10 12 5 17" />
 | 
				
			||||||
 | 
					                        <line x1="13" y1="17" x2="19" y2="17" />
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="text-2xl font-semibold text-white">Powerful API</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="mt-1 text-base leading-7 text-gray-300">
 | 
				
			||||||
 | 
					                Programatically deploy, query, and manage your servers & resources.
 | 
				
			||||||
 | 
					                Integrate to your CI/CD pipelines, or build your own custom integrations. <span
 | 
				
			||||||
 | 
					                    class="text-warning">*</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-4 mb-4">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
 | 
				
			||||||
 | 
					                    <svg width="512" height="512" class="icon" viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					                        xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					                        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
 | 
				
			||||||
 | 
					                            stroke-width="2">
 | 
				
			||||||
 | 
					                            <path
 | 
				
			||||||
 | 
					                                d="M4 18a2 2 0 1 0 4 0a2 2 0 1 0-4 0M4 6a2 2 0 1 0 4 0a2 2 0 1 0-4 0m12 12a2 2 0 1 0 4 0a2 2 0 1 0-4 0M6 8v8" />
 | 
				
			||||||
 | 
					                            <path d="M11 6h5a2 2 0 0 1 2 2v8" />
 | 
				
			||||||
 | 
					                            <path d="m14 9l-3-3l3-3" />
 | 
				
			||||||
 | 
					                        </g>
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="text-2xl font-semibold text-white">Push to Deploy</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="mt-1 text-base leading-7 text-gray-300">
 | 
				
			||||||
 | 
					                Git integration is default today. We support hosted (github.com,
 | 
				
			||||||
 | 
					                gitlab.com<span class="inline-block text-warning">*</span>) or self-hosted<span class="text-warning">*</span>
 | 
				
			||||||
 | 
					                (Github Enterprise, Gitlab) Git repositories.
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-4 mb-4">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
 | 
				
			||||||
 | 
					                    <svg width="512" height="512" class="icon" viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					                        xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					                        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
 | 
				
			||||||
 | 
					                            stroke-width="2"
 | 
				
			||||||
 | 
					                            d="M10 13a2 2 0 1 0 4 0a2 2 0 0 0-4 0m-2 8v-1a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1M15 5a2 2 0 1 0 4 0a2 2 0 0 0-4 0m2 5h2a2 2 0 0 1 2 2v1M5 5a2 2 0 1 0 4 0a2 2 0 0 0-4 0m-2 8v-1a2 2 0 0 1 2-2h2" />
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="text-2xl font-semibold text-white">Pull Request Deployments</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="mt-1 text-base leading-7 text-gray-300">
 | 
				
			||||||
 | 
					                Automagically deploy new commits and pull requests separately to quickly
 | 
				
			||||||
 | 
					                review contributions and speed up your teamwork!
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="pt-20 text-xs">
 | 
				
			||||||
 | 
					        <span class="text-warning">*</span> Some features are work in progress and will be available soon.
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
@isset($other)
 | 
					@isset($other)
 | 
				
			||||||
    {{ $other }}
 | 
					    {{ $other }}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
<div class="pb-6">
 | 
					<div class="pb-6">
 | 
				
			||||||
 | 
					        <livewire:server.proxy.modal :server="$server" />
 | 
				
			||||||
    <div class="flex items-center gap-2">
 | 
					    <div class="flex items-center gap-2">
 | 
				
			||||||
        <h1>Server</h1>
 | 
					        <h1>Server</h1>
 | 
				
			||||||
        @if ($server->settings->is_reachable)
 | 
					        @if ($server->settings->is_reachable)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,5 @@ Please [click here]({{ $invitation_link }}) to accept the invitation.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
If you have any questions, please contact the team owner.<br><br>
 | 
					If you have any questions, please contact the team owner.<br><br>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If it was not you who requested this invitation, please ignore this email, or instantly revoke the invitation by clicking [here]({{ $invitation_link }}/revoke).
 | 
					If it was not you who requested this invitation, please ignore this email.
 | 
				
			||||||
 | 
					 | 
				
			||||||
</x-emails.layout>
 | 
					</x-emails.layout>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,8 @@
 | 
				
			|||||||
                    Copy from Instance Settings
 | 
					                    Copy from Instance Settings
 | 
				
			||||||
                </x-forms.button>
 | 
					                </x-forms.button>
 | 
				
			||||||
            @endif
 | 
					            @endif
 | 
				
			||||||
            @if (isEmailEnabled($team) && auth()->user()->isAdminFromSession())
 | 
					            @if (isEmailEnabled($team) &&
 | 
				
			||||||
 | 
					                    auth()->user()->isAdminFromSession())
 | 
				
			||||||
                <x-forms.button onclick="sendTestEmail.showModal()"
 | 
					                <x-forms.button onclick="sendTestEmail.showModal()"
 | 
				
			||||||
                    class="text-white normal-case btn btn-xs no-animation btn-primary">
 | 
					                    class="text-white normal-case btn btn-xs no-animation btn-primary">
 | 
				
			||||||
                    Send Test Email
 | 
					                    Send Test Email
 | 
				
			||||||
@@ -51,19 +52,15 @@
 | 
				
			|||||||
            </x-forms.button>
 | 
					            </x-forms.button>
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
        <div class="flex flex-col gap-4">
 | 
					        <div class="flex flex-col gap-4">
 | 
				
			||||||
            <details class="border rounded collapse border-coolgray-500 collapse-arrow ">
 | 
					            <div class="p-4 border border-coolgray-500">
 | 
				
			||||||
                <summary class="text-xl collapse-title">
 | 
					                <h3>SMTP Server</h3>
 | 
				
			||||||
                    <div>SMTP Server</div>
 | 
					 | 
				
			||||||
                <div class="w-32">
 | 
					                <div class="w-32">
 | 
				
			||||||
                    <x-forms.checkbox instantSave id="team.smtp_enabled" label="Enabled" />
 | 
					                    <x-forms.checkbox instantSave id="team.smtp_enabled" label="Enabled" />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                </summary>
 | 
					 | 
				
			||||||
                <div class="collapse-content">
 | 
					 | 
				
			||||||
                <form wire:submit.prevent='submit' class="flex flex-col">
 | 
					                <form wire:submit.prevent='submit' class="flex flex-col">
 | 
				
			||||||
                    <div class="flex flex-col gap-4">
 | 
					                    <div class="flex flex-col gap-4">
 | 
				
			||||||
                        <div class="flex flex-col w-full gap-2 xl:flex-row">
 | 
					                        <div class="flex flex-col w-full gap-2 xl:flex-row">
 | 
				
			||||||
                                <x-forms.input required id="team.smtp_host" placeholder="smtp.mailgun.org"
 | 
					                            <x-forms.input required id="team.smtp_host" placeholder="smtp.mailgun.org" label="Host" />
 | 
				
			||||||
                                    label="Host" />
 | 
					 | 
				
			||||||
                            <x-forms.input required id="team.smtp_port" placeholder="587" label="Port" />
 | 
					                            <x-forms.input required id="team.smtp_port" placeholder="587" label="Port" />
 | 
				
			||||||
                            <x-forms.input id="team.smtp_encryption" helper="If SMTP uses SSL, set it to 'tls'."
 | 
					                            <x-forms.input id="team.smtp_encryption" helper="If SMTP uses SSL, set it to 'tls'."
 | 
				
			||||||
                                placeholder="tls" label="Encryption" />
 | 
					                                placeholder="tls" label="Encryption" />
 | 
				
			||||||
@@ -82,15 +79,11 @@
 | 
				
			|||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </form>
 | 
					                </form>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            </details>
 | 
					            <div class="p-4 border border-coolgray-500">
 | 
				
			||||||
            <details class="border rounded collapse border-coolgray-500 collapse-arrow">
 | 
					                <h3>Resend</h3>
 | 
				
			||||||
                <summary class="text-xl collapse-title">
 | 
					 | 
				
			||||||
                    <div>Resend</div>
 | 
					 | 
				
			||||||
                <div class="w-32">
 | 
					                <div class="w-32">
 | 
				
			||||||
                    <x-forms.checkbox instantSave='instantSaveResend' id="team.resend_enabled" label="Enabled" />
 | 
					                    <x-forms.checkbox instantSave='instantSaveResend' id="team.resend_enabled" label="Enabled" />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                </summary>
 | 
					 | 
				
			||||||
                <div class="collapse-content">
 | 
					 | 
				
			||||||
                <form wire:submit.prevent='submitResend' class="flex flex-col">
 | 
					                <form wire:submit.prevent='submitResend' class="flex flex-col">
 | 
				
			||||||
                    <div class="flex flex-col gap-4">
 | 
					                    <div class="flex flex-col gap-4">
 | 
				
			||||||
                        <div class="flex flex-col w-full gap-2 xl:flex-row">
 | 
					                        <div class="flex flex-col w-full gap-2 xl:flex-row">
 | 
				
			||||||
@@ -105,7 +98,6 @@
 | 
				
			|||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </form>
 | 
					                </form>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            </details>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
    @if (isEmailEnabled($team) || data_get($team, 'use_instance_email_settings'))
 | 
					    @if (isEmailEnabled($team) || data_get($team, 'use_instance_email_settings'))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,13 @@
 | 
				
			|||||||
            <x-forms.input id="name" label="Name" required />
 | 
					            <x-forms.input id="name" label="Name" required />
 | 
				
			||||||
            <x-forms.input id="description" label="Description" />
 | 
					            <x-forms.input id="description" label="Description" />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <x-forms.textarea id="value" rows="10" placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"
 | 
					        <x-forms.textarea realtimeValidation id="value" rows="10"
 | 
				
			||||||
            label="Private Key" required />
 | 
					            placeholder="-----BEGIN OPENSSH PRIVATE KEY-----" label="Private Key" required />
 | 
				
			||||||
 | 
					        <x-forms.button wire:click="generateNewKey">Generate new SSH key for me</x-forms.button>
 | 
				
			||||||
 | 
					        <x-forms.textarea id="publicKey" rows="6" readonly label="Public Key" />
 | 
				
			||||||
 | 
					        <span class="font-bold text-warning">ACTION REQUIRED: Copy the 'Public Key' to your server's
 | 
				
			||||||
 | 
					            ~/.ssh/authorized_keys
 | 
				
			||||||
 | 
					            file.</span>
 | 
				
			||||||
        <x-forms.button type="submit">
 | 
					        <x-forms.button type="submit">
 | 
				
			||||||
            Save Private Key
 | 
					            Save Private Key
 | 
				
			||||||
        </x-forms.button>
 | 
					        </x-forms.button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,16 +1,6 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    @if ($server->isFunctional())
 | 
					    @if ($server->isFunctional())
 | 
				
			||||||
        @if (data_get($server,'proxy.type'))
 | 
					        @if (data_get($server,'proxy.type'))
 | 
				
			||||||
        <x-modal submitWireAction="proxyStatusUpdated" modalId="startProxy">
 | 
					 | 
				
			||||||
            <x-slot:modalBody>
 | 
					 | 
				
			||||||
                <livewire:activity-monitor header="Proxy Startup Logs" />
 | 
					 | 
				
			||||||
            </x-slot:modalBody>
 | 
					 | 
				
			||||||
            <x-slot:modalSubmit>
 | 
					 | 
				
			||||||
                <x-forms.button onclick="startProxy.close()" type="submit">
 | 
					 | 
				
			||||||
                    Close
 | 
					 | 
				
			||||||
                </x-forms.button>
 | 
					 | 
				
			||||||
            </x-slot:modalSubmit>
 | 
					 | 
				
			||||||
        </x-modal>
 | 
					 | 
				
			||||||
            <div x-init="$wire.loadProxyConfiguration">
 | 
					            <div x-init="$wire.loadProxyConfiguration">
 | 
				
			||||||
                @if ($selectedProxy === 'TRAEFIK_V2')
 | 
					                @if ($selectedProxy === 'TRAEFIK_V2')
 | 
				
			||||||
                    <form wire:submit.prevent='submit'>
 | 
					                    <form wire:submit.prevent='submit'>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								resources/views/livewire/server/proxy/modal.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								resources/views/livewire/server/proxy/modal.blade.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<div>
 | 
				
			||||||
 | 
					    <x-modal submitWireAction="proxyStatusUpdated" modalId="startProxy">
 | 
				
			||||||
 | 
					        <x-slot:modalBody>
 | 
				
			||||||
 | 
					            <livewire:activity-monitor header="Proxy Startup Logs" />
 | 
				
			||||||
 | 
					        </x-slot:modalBody>
 | 
				
			||||||
 | 
					        <x-slot:modalSubmit>
 | 
				
			||||||
 | 
					            <x-forms.button onclick="startProxy.close()" type="submit">
 | 
				
			||||||
 | 
					                Close
 | 
				
			||||||
 | 
					            </x-forms.button>
 | 
				
			||||||
 | 
					        </x-slot:modalSubmit>
 | 
				
			||||||
 | 
					    </x-modal>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<div class="flex gap-2" x-init="$wire.getProxyStatus">
 | 
					<div class="flex gap-2" x-init="$wire.getProxyStatus">
 | 
				
			||||||
    @if ($server->proxy->status === 'running')
 | 
					    @if ($server->proxy->status === 'running')
 | 
				
			||||||
        <x-status.running text="Proxy Running" />
 | 
					        <x-status.running text="Proxy Running" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
<x-layout-subscription>
 | 
					<x-layout-subscription>
 | 
				
			||||||
    @if ($settings->is_resale_license_active)
 | 
					    @if ($settings->is_resale_license_active)
 | 
				
			||||||
 | 
					        @if (auth()->user()->isAdminFromSession())
 | 
				
			||||||
            <div class="flex justify-center mx-10">
 | 
					            <div class="flex justify-center mx-10">
 | 
				
			||||||
                <div x-data>
 | 
					                <div x-data>
 | 
				
			||||||
                    <div class="flex gap-2">
 | 
					                    <div class="flex gap-2">
 | 
				
			||||||
@@ -17,7 +18,8 @@
 | 
				
			|||||||
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
 | 
					                                <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" />
 | 
					                                    d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
 | 
				
			||||||
                            </svg>
 | 
					                            </svg>
 | 
				
			||||||
                        <span>Something went wrong with your subscription. Please try again or contact support.</span>
 | 
					                            <span>Something went wrong with your subscription. Please try again or contact
 | 
				
			||||||
 | 
					                                support.</span>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    @endif
 | 
					                    @endif
 | 
				
			||||||
                    @if (config('subscription.provider') !== null)
 | 
					                    @if (config('subscription.provider') !== null)
 | 
				
			||||||
@@ -26,6 +28,19 @@
 | 
				
			|||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        @else
 | 
					        @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>
 | 
				
			||||||
 | 
					                    <livewire:switch-team />
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="flex items-center pb-8">
 | 
				
			||||||
 | 
					                    <span>Currently active team: <span
 | 
				
			||||||
 | 
					                            class="text-warning">{{ session('currentTeam.name') }}</span></span>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div>You are not an admin or have been removed from this team. If this does not make sense, please <span class="text-white underline cursor-pointer" wire:click="help" onclick="help.showModal()">contact us</span>.</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        @endif
 | 
				
			||||||
 | 
					    @else
 | 
				
			||||||
 | 
					        <div class="px-10" >Resale license is not active. Please contact your instance admin.</div>
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
</x-layout-subscription>
 | 
					</x-layout-subscription>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
            "version": "3.12.36"
 | 
					            "version": "3.12.36"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "v4": {
 | 
					        "v4": {
 | 
				
			||||||
            "version": "4.0.0-beta.36"
 | 
					            "version": "4.0.0-beta.37"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user