Merge pull request #1238 from coollabsio/next

Fixes
This commit is contained in:
Andras Bacsai 2023-09-18 15:31:54 +02:00 committed by GitHub
commit b7acf99cde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 301 additions and 405 deletions

View File

@ -16,9 +16,12 @@ class CheckConfiguration
"cat $proxy_path/docker-compose.yml", "cat $proxy_path/docker-compose.yml",
], $server, false); ], $server, false);
if ($reset || is_null($proxy_configuration)) { if ($reset || !$proxy_configuration || 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;
} }
if (!$proxy_configuration || is_null($proxy_configuration)) {
throw new \Exception("Could not generate proxy configuration");
}
return $proxy_configuration; return $proxy_configuration;
} }
} }

View File

@ -51,9 +51,9 @@ class StartProxy
"cd $proxy_path", "cd $proxy_path",
"echo '####### Creating Docker Compose file...'", "echo '####### Creating Docker Compose file...'",
"echo '####### Pulling docker image...'", "echo '####### Pulling docker image...'",
'docker compose pull', 'docker compose pull || docker-compose pull',
"echo '####### Stopping existing coolify-proxy...'", "echo '####### Stopping existing coolify-proxy...'",
"docker compose down -v --remove-orphans > /dev/null 2>&1 || true", "docker compose down -v --remove-orphans > /dev/null 2>&1 || docker-compose down -v --remove-orphans > /dev/null 2>&1 || true",
"command -v fuser >/dev/null || command -v lsof >/dev/null || echo '####### Could not kill existing processes listening on port 80 & 443. Please stop the process holding these ports...'", "command -v fuser >/dev/null || command -v lsof >/dev/null || echo '####### Could not kill existing processes listening on port 80 & 443. Please stop the process holding these ports...'",
"command -v lsof >/dev/null && lsof -nt -i:80 | xargs -r kill -9 || true", "command -v lsof >/dev/null && lsof -nt -i:80 | xargs -r kill -9 || true",
"command -v lsof >/dev/null && lsof -nt -i:443 | xargs -r kill -9 || true", "command -v lsof >/dev/null && lsof -nt -i:443 | xargs -r kill -9 || true",
@ -63,7 +63,7 @@ class StartProxy
"systemctl disable apache2 > /dev/null 2>&1 || true", "systemctl disable apache2 > /dev/null 2>&1 || true",
"systemctl disable apache > /dev/null 2>&1 || true", "systemctl disable apache > /dev/null 2>&1 || true",
"echo '####### Starting coolify-proxy...'", "echo '####### Starting coolify-proxy...'",
'docker compose up -d --remove-orphans', 'docker compose up -d --remove-orphans || docker-compose up -d --remove-orphans',
"echo '####### Proxy installed successfully...'" "echo '####### Proxy installed successfully...'"
]; ];
if (!$async) { if (!$async) {

View File

@ -7,7 +7,7 @@ use App\Models\StandaloneDocker;
class InstallDocker class InstallDocker
{ {
public function __invoke(Server $server, bool $instant = false) public function __invoke(Server $server)
{ {
$dockerVersion = '24.0'; $dockerVersion = '24.0';
$config = base64_encode('{ $config = base64_encode('{
@ -55,9 +55,6 @@ class InstallDocker
"echo '####### Done!'" "echo '####### Done!'"
]; ];
} }
if ($instant) {
return instant_remote_process($command, $server);
}
return remote_process($command, $server); return remote_process($command, $server);
} }
} }

View File

@ -17,8 +17,11 @@ class CoolifyTaskArgs extends Data
public string $type, public string $type,
public ?string $type_uuid = null, public ?string $type_uuid = null,
public ?Model $model = null, public ?Model $model = null,
public string $status = ProcessStatus::QUEUED->value, public ?string $status = null ,
public bool $ignore_errors = false, public bool $ignore_errors = false,
) { ) {
if(is_null($status)){
$this->status = ProcessStatus::QUEUED->value;
}
} }
} }

View File

@ -9,7 +9,6 @@ use App\Models\Server;
use App\Models\Team; use App\Models\Team;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Livewire\Component; use Livewire\Component;
use Visus\Cuid2\Cuid2;
class Index extends Component class Index extends Component
{ {
@ -40,8 +39,8 @@ class Index extends Component
public bool $dockerInstallationStarted = false; public bool $dockerInstallationStarted = false;
public string $localhostPublicKey; public string $serverPublicKey;
public bool $localhostReachable = true; public bool $serverReachable = true;
public function mount() public function mount()
{ {
@ -70,14 +69,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
public function restartBoarding() public function restartBoarding()
{ {
// if ($this->selectedServerType !== 'localhost') {
// if ($this->createdServer) {
// $this->createdServer->delete();
// }
// if ($this->createdPrivateKey) {
// $this->createdPrivateKey->delete();
// }
// }
return redirect()->route('boarding'); return redirect()->route('boarding');
} }
public function skipBoarding() public function skipBoarding()
@ -98,7 +89,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
if (!$this->createdServer) { if (!$this->createdServer) {
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.'); return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
} }
$this->localhostPublicKey = $this->createdServer->privateKey->publicKey(); $this->serverPublicKey = $this->createdServer->privateKey->publicKey();
return $this->validateServer('localhost'); return $this->validateServer('localhost');
} elseif ($this->selectedServerType === 'remote') { } elseif ($this->selectedServerType === 'remote') {
$this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get(); $this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get();
@ -123,6 +114,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
return; return;
} }
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id; $this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
$this->validateServer(); $this->validateServer();
} }
public function getProxyType() public function getProxyType()
@ -201,11 +193,11 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
instant_remote_process(['uptime'], $this->createdServer, true); instant_remote_process(['uptime'], $this->createdServer, true);
$this->createdServer->settings->update([ $this->createdServer->settings()->update([
'is_reachable' => true, 'is_reachable' => true,
]); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->localhostReachable = false; $this->serverReachable = false;
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this); return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
} }
@ -216,8 +208,12 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->currentState = 'install-docker'; $this->currentState = 'install-docker';
throw new \Exception('Docker version is not supported or not installed.'); throw new \Exception('Docker version is not supported or not installed.');
} }
$this->dockerInstalledOrSkipped(); $this->createdServer->settings()->update([
'is_usable' => true,
]);
$this->getProxyType();
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->dockerInstallationStarted = false;
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this); return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
} }
} }
@ -229,10 +225,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
} }
public function dockerInstalledOrSkipped() public function dockerInstalledOrSkipped()
{ {
$this->createdServer->settings->update([ $this->validateServer();
'is_usable' => true,
]);
$this->getProxyType();
} }
public function selectProxy(string|null $proxyType = null) public function selectProxy(string|null $proxyType = null)
{ {

View File

@ -86,7 +86,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server)); $this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
} }
} else { } else {
ray($foundProxyContainer);
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status'); $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
$this->server->save(); $this->server->save();
} }
@ -198,94 +197,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid; $url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid;
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url)); $this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
} }
return;
foreach ($applications as $application) {
$uuid = data_get($application, 'uuid');
$id = data_get($application, 'id');
$foundContainer = $containers->filter(function ($value, $key) use ($id, $uuid) {
$labels = data_get($value, 'Config.Labels');
$labels = Arr::undot(format_docker_labels_to_json($labels));
$labelId = data_get($labels, 'coolify.applicationId');
if ($labelId == $id) {
return $value;
}
$isPR = Str::startsWith(data_get($value, 'Name'), "/$uuid");
$isPR = Str::contains(data_get($value, 'Name'), "-pr-");
if ($isPR) {
return false;
}
return $value;
})->first();
if ($foundContainer) {
$containerStatus = data_get($foundContainer, 'State.Status');
$databaseStatus = data_get($application, 'status');
if ($containerStatus !== $databaseStatus) {
$application->update(['status' => $containerStatus]);
}
} else {
$databaseStatus = data_get($application, 'status');
if ($databaseStatus !== 'exited') {
$application->update(['status' => 'exited']);
$name = data_get($application, 'name');
$fqdn = data_get($application, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn;
$project = data_get($application, 'environment.project');
$environment = data_get($application, 'environment');
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $application->uuid;
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
}
}
$previews = $application->previews;
foreach ($previews as $preview) {
$foundContainer = $containers->filter(function ($value, $key) use ($id, $uuid, $preview) {
$labels = data_get($value, 'Config.Labels');
$labels = Arr::undot(format_docker_labels_to_json($labels));
$labelId = data_get($labels, 'coolify.applicationId');
if ($labelId == "$id-pr-{$preview->id}") {
return $value;
}
return Str::startsWith(data_get($value, 'Name'), "/$uuid-pr-{$preview->id}");
})->first();
}
}
foreach ($databases as $database) {
$uuid = data_get($database, 'uuid');
$foundContainer = $containers->filter(function ($value, $key) use ($uuid) {
return Str::startsWith(data_get($value, 'Name'), "/$uuid");
})->first();
if ($foundContainer) {
$containerStatus = data_get($foundContainer, 'State.Status');
$databaseStatus = data_get($database, 'status');
if ($containerStatus !== $databaseStatus) {
$database->update(['status' => $containerStatus]);
}
} else {
$databaseStatus = data_get($database, 'status');
if ($databaseStatus !== 'exited') {
$database->update(['status' => 'exited']);
$name = data_get($database, 'name');
$containerName = $name;
$project = data_get($database, 'environment.project');
$environment = data_get($database, 'environment');
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid;
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
}
}
}
// TODO Monitor other containers not managed by Coolify
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage()); send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage());
ray($e->getMessage()); ray($e->getMessage());

View File

@ -14,7 +14,7 @@ class DeploymentFailed extends Notification implements ShouldQueue
{ {
use Queueable; use Queueable;
public $tries = 5; public $tries = 1;
public Application $application; public Application $application;
public ?ApplicationPreview $preview = null; public ?ApplicationPreview $preview = null;

View File

@ -14,7 +14,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue
{ {
use Queueable; use Queueable;
public $tries = 5; public $tries = 1;
public Application $application; public Application $application;
public ApplicationPreview|null $preview = null; public ApplicationPreview|null $preview = null;

View File

@ -13,7 +13,7 @@ class StatusChanged extends Notification implements ShouldQueue
{ {
use Queueable; use Queueable;
public $tries = 5; public $tries = 1;
public Application $application; public Application $application;
public string $application_name; public string $application_name;

View File

@ -6,14 +6,16 @@ use Exception;
use Illuminate\Mail\Message; use Illuminate\Mail\Message;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Log;
class EmailChannel class EmailChannel
{ {
public function send(SendsEmail $notifiable, Notification $notification): void public function send(SendsEmail $notifiable, Notification $notification): void
{ {
try {
$this->bootConfigs($notifiable); $this->bootConfigs($notifiable);
$recepients = $notifiable->getRecepients($notification); $recepients = $notifiable->getRecepients($notification);
ray($recepients);
if (count($recepients) === 0) { if (count($recepients) === 0) {
throw new Exception('No email recipients found'); throw new Exception('No email recipients found');
} }
@ -27,6 +29,11 @@ class EmailChannel
->subject($mailMessage->subject) ->subject($mailMessage->subject)
->html((string)$mailMessage->render()) ->html((string)$mailMessage->render())
); );
} catch (Exception $e) {
ray($e->getMessage());
send_internal_notification("EmailChannel error: {$e->getMessage()}. Failed to send email to: " . implode(', ', $recepients) . " with subject: {$mailMessage->subject}");
throw $e;
}
} }
private function bootConfigs($notifiable): void private function bootConfigs($notifiable): void

View File

@ -12,7 +12,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
{ {
use Queueable; use Queueable;
public $tries = 5; public $tries = 1;
public function __construct(public string $name, public Server $server, public ?string $url = null) public function __construct(public string $name, public Server $server, public ?string $url = null)

View File

@ -3,8 +3,6 @@
namespace App\Notifications\Database; namespace App\Notifications\Database;
use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledDatabaseBackup;
use App\Notifications\Channels\DiscordChannel;
use App\Notifications\Channels\EmailChannel;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@ -14,7 +12,7 @@ class BackupFailed extends Notification implements ShouldQueue
{ {
use Queueable; use Queueable;
public $tries = 5; public $tries = 1;
public string $name; public string $name;
public string $frequency; public string $frequency;

View File

@ -3,8 +3,6 @@
namespace App\Notifications\Database; namespace App\Notifications\Database;
use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledDatabaseBackup;
use App\Notifications\Channels\DiscordChannel;
use App\Notifications\Channels\EmailChannel;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@ -14,7 +12,7 @@ class BackupSuccess extends Notification implements ShouldQueue
{ {
use Queueable; use Queueable;
public $tries = 5; public $tries = 1;
public string $name; public string $name;
public string $frequency; public string $frequency;

View File

@ -18,12 +18,14 @@ use Spatie\Activitylog\Contracts\Activity;
function remote_process( function remote_process(
array $command, array $command,
Server $server, Server $server,
string $type = ActivityTypes::INLINE->value, ?string $type = null,
?string $type_uuid = null, ?string $type_uuid = null,
?Model $model = null, ?Model $model = null,
bool $ignore_errors = false, bool $ignore_errors = false,
): Activity { ): Activity {
if (is_null($type)) {
$type = ActivityTypes::INLINE->value;
}
$command_string = implode("\n", $command); $command_string = implode("\n", $command);
if (auth()->user()) { if (auth()->user()) {
$teams = auth()->user()->teams->pluck('id'); $teams = auth()->user()->teams->pluck('id');

View File

@ -138,5 +138,6 @@ function allowedPathsForBoardingAccounts()
...allowedPathsForUnsubscribedAccounts(), ...allowedPathsForUnsubscribedAccounts(),
'boarding', 'boarding',
'livewire/message/boarding.index', 'livewire/message/boarding.index',
'livewire/message/activity-monitor'
]; ];
} }

View File

@ -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.42', 'release' => '4.0.0-beta.43',
// 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'),

View File

@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.42'; return '4.0.0-beta.43';

View File

@ -1,7 +1,7 @@
@props([ @props([
'showSubscribeButtons' => true, 'showSubscribeButtons' => true,
]) ])
<div x-data="{ selected: 'yearly' }" class="w-full pb-20"> <div x-data="{ selected: 'monthly' }" class="w-full pb-20">
<div class="px-6 mx-auto lg:px-8"> <div class="px-6 mx-auto lg:px-8">
<div class="flex justify-center"> <div class="flex justify-center">
<fieldset <fieldset
@ -24,80 +24,36 @@
<div class="py-2 text-center"><span class="font-bold text-warning">{{ config('constants.limits.trial_period') }} <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> 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">10%</span> annually with the yearly plans.
</div> </div>
</div> </div>
<div x-show="selected === 'yearly'" class="flex justify-center h-10 mt-3 text-sm leading-6 "> <div x-show="selected === 'yearly'" class="flex justify-center h-10 mt-3 text-sm leading-6 ">
<div> <div>
</div> </div>
</div> </div>
<div class="p-4 rounded bg-coolgray-400">
<h2 id="tier-hobby" class="flex items-start gap-4 text-4xl font-bold tracking-tight">Unlimited Trial
<x-forms.button><a class="font-bold text-white hover:no-underline"
href="https://github.com/coollabsio/coolify">Get Started</a></x-forms.button>
</h2>
<p class="mt-4 text-sm leading-6">Start self-hosting <span class="text-warning">without limits</span> with
our
OSS version. Same features as the paid version, but you have to manage by yourself.</p>
</div>
<div class="flow-root mt-12"> <div class="flow-root mt-12">
<div <div
class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap-y-16 sm:mx-auto lg:-mx-8 lg:mt-0 lg:max-w-none lg:grid-cols-4 lg:divide-x lg:divide-y-0 xl:-mx-4"> class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap-y-16 sm:mx-auto lg:-mx-8 lg:mt-0 lg:max-w-none lg:grid-cols-3 lg:divide-x lg:divide-y-0 xl:-mx-4">
<div class="px-8 pt-16 lg:pt-0">
<h3 id="tier-trial" class="text-base font-semibold leading-7 text-white">Unlimited Trial</h3>
<p class="flex items-baseline mt-6 gap-x-1">
<span x-show="selected === 'monthly'" x-cloak>
<span class="text-4xl font-bold tracking-tight text-white">Free</span>
</span>
<span x-show="selected === 'yearly'" x-cloak>
<span class="text-4xl font-bold tracking-tight text-white">Still Free </span>
</span>
</p>
<span x-show="selected === 'monthly'" x-cloak>
<span>billed monthly</span>
</span>
<span x-show="selected === 'yearly'" x-cloak>
<span>billed annually</span>
</span>
<a href="https://github.com/coollabsio/coolify" aria-describedby="tier-trial" class="buyme">Get
Started</a>
<p class="mt-10 text-sm leading-6 text-white h-[6.5rem]">Start self-hosting without limits with our
OSS
version.</p>
<ul role="list" class="space-y-3 text-sm leading-6 ">
<li class="flex gap-x-3">
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" />
</svg>
You manage everything
</li>
<li class="flex gap-x-3">
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" />
</svg>
Community Support
</li>
<li class="flex font-bold text-white gap-x-3">
<svg width="512" height="512" class="flex-none w-5 h-6 text-green-600"
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 13a8 8 0 0 1 7 7a6 6 0 0 0 3-5a9 9 0 0 0 6-8a3 3 0 0 0-3-3a9 9 0 0 0-8 6a6 6 0 0 0-5 3" />
<path d="M7 14a6 6 0 0 0-3 6a6 6 0 0 0 6-3m4-8a1 1 0 1 0 2 0a1 1 0 1 0-2 0" />
</g>
</svg>
+ All upcoming features
</li>
</ul>
</div>
<div class="pt-16 lg:px-8 lg:pt-0 xl:px-14"> <div class="pt-16 lg:px-8 lg:pt-0 xl:px-14">
<h3 id="tier-basic" class="text-base font-semibold leading-7 text-white">Basic</h3> <h3 id="tier-basic" class="text-base font-semibold leading-7 text-white">Basic</h3>
<p class="flex items-baseline mt-6 gap-x-1"> <p class="flex items-baseline mt-6 gap-x-1">
<span x-show="selected === 'monthly'" x-cloak> <span x-show="selected === 'monthly'" x-cloak>
<span class="text-4xl font-bold tracking-tight text-white">$5</span> <span class="text-4xl font-bold tracking-tight text-white">$5</span>
<span class="text-sm font-semibold leading-6 ">/month</span> <span class="text-sm font-semibold leading-6 ">/month + VAT</span>
</span> </span>
<span x-show="selected === 'yearly'" x-cloak> <span x-show="selected === 'yearly'" x-cloak>
<span class="text-4xl font-bold tracking-tight text-white">$4</span> <span class="text-4xl font-bold tracking-tight text-white">$4</span>
<span class="text-sm font-semibold leading-6 ">/month</span> <span class="text-sm font-semibold leading-6 ">/month + VAT</span>
</span> </span>
</p> </p>
<span x-show="selected === 'monthly'" x-cloak> <span x-show="selected === 'monthly'" x-cloak>
@ -139,8 +95,8 @@
<li class="flex font-bold text-white gap-x-3"> <li class="flex font-bold text-white gap-x-3">
<svg width="512" height="512" class="flex-none w-5 h-6 text-green-600" <svg width="512" height="512" class="flex-none w-5 h-6 text-green-600"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-linejoin="round" stroke-width="2"> stroke-width="2">
<path <path
d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3-5a9 9 0 0 0 6-8a3 3 0 0 0-3-3a9 9 0 0 0-8 6a6 6 0 0 0-5 3" /> d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3-5a9 9 0 0 0 6-8a3 3 0 0 0-3-3a9 9 0 0 0-8 6a6 6 0 0 0-5 3" />
<path d="M7 14a6 6 0 0 0-3 6a6 6 0 0 0 6-3m4-8a1 1 0 1 0 2 0a1 1 0 1 0-2 0" /> <path d="M7 14a6 6 0 0 0-3 6a6 6 0 0 0 6-3m4-8a1 1 0 1 0 2 0a1 1 0 1 0-2 0" />
@ -154,12 +110,12 @@
<h3 id="tier-pro" class="text-base font-semibold leading-7 text-white">Pro</h3> <h3 id="tier-pro" class="text-base font-semibold leading-7 text-white">Pro</h3>
<p class="flex items-baseline mt-6 gap-x-1"> <p class="flex items-baseline mt-6 gap-x-1">
<span x-show="selected === 'monthly'" x-cloak> <span x-show="selected === 'monthly'" x-cloak>
<span class="text-4xl font-bold tracking-tight text-white">$29</span> <span class="text-4xl font-bold tracking-tight text-white">$30</span>
<span class="text-sm font-semibold leading-6 ">/month</span> <span class="text-sm font-semibold leading-6 ">/month + VAT</span>
</span> </span>
<span x-show="selected === 'yearly'" x-cloak> <span x-show="selected === 'yearly'" x-cloak>
<span class="text-4xl font-bold tracking-tight text-white">$26</span> <span class="text-4xl font-bold tracking-tight text-white">$27</span>
<span class="text-sm font-semibold leading-6 ">/month</span> <span class="text-sm font-semibold leading-6 ">/month + VAT</span>
</span> </span>
</p> </p>
<span x-show="selected === 'monthly'" x-cloak> <span x-show="selected === 'monthly'" x-cloak>
@ -192,7 +148,7 @@
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" /> clip-rule="evenodd" />
</svg> </svg>
Basic Support Included Email System
</li> </li>
<li class="flex gap-x-3"> <li class="flex gap-x-3">
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor" <svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
@ -201,7 +157,7 @@
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" /> clip-rule="evenodd" />
</svg> </svg>
Included Email System Email Support
</li> </li>
<li class="flex font-bold text-white gap-x-3"> <li class="flex font-bold text-white gap-x-3">
<svg width="512" height="512" class="flex-none w-5 h-6 text-green-600" <svg width="512" height="512" class="flex-none w-5 h-6 text-green-600"
@ -221,12 +177,12 @@
<h3 id="tier-ultimate" class="text-base font-semibold leading-7 text-white">Ultimate</h3> <h3 id="tier-ultimate" class="text-base font-semibold leading-7 text-white">Ultimate</h3>
<p class="flex items-baseline mt-6 gap-x-1"> <p class="flex items-baseline mt-6 gap-x-1">
<span x-show="selected === 'monthly'" x-cloak> <span x-show="selected === 'monthly'" x-cloak>
<span class="text-4xl font-bold tracking-tight text-white">$69</span> <span class="text-4xl font-bold tracking-tight text-white">$?</span>
<span class="text-sm font-semibold leading-6 ">/month</span> <span class="text-sm font-semibold leading-6 ">/month + VAT</span>
</span> </span>
<span x-show="selected === 'yearly'" x-cloak> <span x-show="selected === 'yearly'" x-cloak>
<span class="text-4xl font-bold tracking-tight text-white">$63</span> <span class="text-4xl font-bold tracking-tight text-white">$?</span>
<span class="text-sm font-semibold leading-6 ">/month</span> <span class="text-sm font-semibold leading-6 ">/month + VAT</span>
</span> </span>
</p> </p>
<span x-show="selected === 'monthly'" x-cloak> <span x-show="selected === 'monthly'" x-cloak>
@ -250,17 +206,9 @@
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" /> clip-rule="evenodd" />
</svg> </svg>
25 servers <x-helper helper="Bring Your Own Server. All you need is n SSH connection." /> ? servers <x-helper helper="Bring Your Own Server. All you need is n SSH connection." />
</li>
<li class="flex font-bold text-white gap-x-3">
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" />
</svg>
Priority Support
</li> </li>
<li class="flex gap-x-3"> <li class="flex gap-x-3">
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor" <svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true"> aria-hidden="true">
@ -270,6 +218,15 @@
</svg> </svg>
Included Email System Included Email System
</li> </li>
<li class="flex font-bold text-white gap-x-3">
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" />
</svg>
Priority (Email/Chat) Support
</li>
<li class="flex font-bold text-white gap-x-3"> <li class="flex font-bold text-white gap-x-3">
<svg width="512" height="512" class="flex-none w-5 h-6 text-green-600" <svg width="512" height="512" class="flex-none w-5 h-6 text-green-600"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
@ -285,8 +242,13 @@
</ul> </ul>
</div> </div>
</div> </div>
<div class="pt-10">Need unlimited servers or official support for your Coolify instance? <a <div class="p-4 mt-10 rounded">
href="https://docs.coollabs.io/contact" class='text-warning'>Contact us.</a> <div class="flex items-start gap-4 text-xl tracking-tight">Need official support for
your self-hosted instance?
<x-forms.button>
<a class="font-bold text-white hover:no-underline"
href="https://docs.coollabs.io/contact">Contact Us</a>
</x-forms.button>
</div> </div>
</div> </div>
</div> </div>
@ -297,8 +259,8 @@
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500"> <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" <svg width="512" height="512" class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" <path fill="none" stroke="currentColor" stroke-linecap="round"
stroke-width="2" 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" /> 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> </svg>
</div> </div>
@ -339,8 +301,10 @@
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"> 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
<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" /> 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> </g>
</svg> </svg>
</div> </div>
@ -356,8 +320,9 @@
<div> <div>
<div class="flex items-center gap-4 mb-4"> <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"> <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" <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path stroke="none" d="M0 0h24v24H0z" fill="none" />
<rect x="3" y="4" width="18" height="12" rx="1" /> <rect x="3" y="4" width="18" height="12" rx="1" />
<path d="M7 20h10" /> <path d="M7 20h10" />
@ -396,8 +361,9 @@
<div> <div>
<div class="flex items-center gap-4 mb-4"> <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"> <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" <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path stroke="none" d="M0 0h24v24H0z" fill="none" />
<polyline points="5 7 10 12 5 17" /> <polyline points="5 7 10 12 5 17" />
<line x1="13" y1="17" x2="19" y2="17" /> <line x1="13" y1="17" x2="19" y2="17" />
@ -429,7 +395,8 @@
</div> </div>
<div class="mt-1 text-base leading-7 text-gray-300"> <div class="mt-1 text-base leading-7 text-gray-300">
Git integration is default today. We support hosted (github.com, 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> gitlab.com<span class="inline-block text-warning">*</span>) or self-hosted<span
class="text-warning">*</span>
(Github Enterprise, Gitlab) Git repositories. (Github Enterprise, Gitlab) Git repositories.
</div> </div>
</div> </div>
@ -438,8 +405,8 @@
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500"> <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" <svg width="512" height="512" class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" <path fill="none" stroke="currentColor" stroke-linecap="round"
stroke-width="2" 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" /> 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> </svg>
</div> </div>

View File

@ -1,8 +1,6 @@
@extends('layouts.base') @extends('layouts.base')
@section('body') @section('body')
<main class="min-h-screen hero"> <x-modal noSubmit modalId="installDocker">
<div class="hero-content">
<x-modal modalId="installDocker">
<x-slot:modalBody> <x-slot:modalBody>
<livewire:activity-monitor header="Docker Installation Logs" /> <livewire:activity-monitor header="Docker Installation Logs" />
</x-slot:modalBody> </x-slot:modalBody>
@ -12,6 +10,8 @@
</x-forms.button> </x-forms.button>
</x-slot:modalSubmit> </x-slot:modalSubmit>
</x-modal> </x-modal>
<main class="min-h-screen hero">
<div class="hero-content">
{{ $slot }} {{ $slot }}
</div> </div>
</main> </main>

View File

@ -50,13 +50,13 @@
<x-forms.button class="justify-center box" wire:target="setServerType('remote')" <x-forms.button class="justify-center box" wire:target="setServerType('remote')"
wire:click="setServerType('remote')">Remote Server wire:click="setServerType('remote')">Remote Server
</x-forms.button> </x-forms.button>
@if (!$localhostReachable) @if (!$serverReachable)
Localhost is not reachable with the following public key. Localhost is not reachable with the following public key.
<br /> <br /> <br /> <br />
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user
'root' or skip the boarding process and add a new private key manually to Coolify and to the 'root' or skip the boarding process and add a new private key manually to Coolify and to the
server. server.
<x-forms.input readonly id="localhostPublicKey"></x-forms.input> <x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="box" wire:target="setServerType('localhost')" <x-forms.button class="box" wire:target="setServerType('localhost')"
wire:click="setServerType('localhost')">Check again wire:click="setServerType('localhost')">Check again
</x-forms.button> </x-forms.button>
@ -130,6 +130,17 @@
<x-forms.button type="submit">Use this Server</x-forms.button> <x-forms.button type="submit">Use this Server</x-forms.button>
</form> </form>
</div> </div>
@if (!$serverReachable)
This server is not reachable with the following public key.
<br /> <br />
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user
'root' or skip the boarding process and add a new private key manually to Coolify and to the
server.
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="box" wire:target="validateServer"
wire:click="validateServer">Check again
</x-forms.button>
@endif
</x-slot:actions> </x-slot:actions>
<x-slot:explanation> <x-slot:explanation>
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p> <p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
@ -214,10 +225,10 @@
Could not find Docker Engine on your server. Do you want me to install it for you? Could not find Docker Engine on your server. Do you want me to install it for you?
</x-slot:question> </x-slot:question>
<x-slot:actions> <x-slot:actions>
@if ($dockerInstallationStarted)
<x-forms.button class="justify-center box" wire:click="installDocker" <x-forms.button class="justify-center box" wire:click="installDocker"
onclick="installDocker.showModal()"> onclick="installDocker.showModal()">
Let's do it!</x-forms.button> Let's do it!</x-forms.button>
@if ($dockerInstallationStarted)
<x-forms.button class="justify-center box" wire:click="dockerInstalledOrSkipped"> <x-forms.button class="justify-center box" wire:click="dockerInstalledOrSkipped">
Next</x-forms.button> Next</x-forms.button>
@endif @endif

View File

@ -2,16 +2,19 @@
@if (config('subscription.provider') === 'stripe') @if (config('subscription.provider') === 'stripe')
<x-slot:basic> <x-slot:basic>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-basic" <x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-basic"
class="w-full h-10 buyme" wire:click="subscribeStripe('basic-monthly')"> {{$isTrial ? 'Start Trial' : 'Subscribe' }} class="w-full h-10 buyme" wire:click="subscribeStripe('basic-monthly')">
{{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button> </x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-basic" <x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-basic"
class="w-full h-10 buyme" wire:click="subscribeStripe('basic-yearly')"> {{$isTrial ? 'Start Trial' : 'Subscribe' }} class="w-full h-10 buyme" wire:click="subscribeStripe('basic-yearly')">
{{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button> </x-forms.button>
</x-slot:basic> </x-slot:basic>
<x-slot:pro> <x-slot:pro>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-pro" <x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-pro"
class="w-full h-10 buyme" wire:click="subscribeStripe('pro-monthly')"> {{$isTrial ? 'Start Trial' : 'Subscribe' }} class="w-full h-10 buyme" wire:click="subscribeStripe('pro-monthly')">
{{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button> </x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-pro" class="w-full h-10 buyme" <x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-pro" class="w-full h-10 buyme"
@ -20,11 +23,13 @@
</x-slot:pro> </x-slot:pro>
<x-slot:ultimate> <x-slot:ultimate>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-ultimate" <x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-ultimate"
class="w-full h-10 buyme" wire:click="subscribeStripe('ultimate-monthly')"> {{$isTrial ? 'Start Trial' : 'Subscribe' }} class="w-full h-10 buyme"><a class="text-white hover:no-underline" href="https://docs.coollabs.io/contact" target="_blank">
Contact Us</a>
</x-forms.button> </x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-ultimate" <x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-ultimate"
class="w-full h-10 buyme" wire:click="subscribeStripe('ultimate-yearly')"> {{$isTrial ? 'Start Trial' : 'Subscribe' }} class="w-full h-10 buyme"><a class="text-white hover:no-underline" href="https://docs.coollabs.io/contact" target="_blank">
Contact Us</a>
</x-forms.button> </x-forms.button>
</x-slot:ultimate> </x-slot:ultimate>
@endif @endif

View File

@ -4,7 +4,7 @@
"version": "3.12.36" "version": "3.12.36"
}, },
"v4": { "v4": {
"version": "4.0.0-beta.42" "version": "4.0.0-beta.43"
} }
} }
} }