Merge pull request #1297 from coollabsio/next

v4.0.0-beta.71
This commit is contained in:
Andras Bacsai 2023-10-09 15:19:35 +02:00 committed by GitHub
commit 6c3f97d9ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 401 additions and 234 deletions

View File

@ -36,7 +36,7 @@ # Installation
## Support
Contact us [here](https://docs.coollabs.io/contact).
Contact us [here](https://coolify.io/contact).
## Recognitions

View File

@ -58,6 +58,11 @@ public function create(array $input): User
'password' => Hash::make($input['password']),
]);
$team = $user->teams()->first();
if (isCloud()) {
$user->sendVerificationEmail();
} else {
$user->markEmailAsVerified();
}
}
// Set session variable
session(['currentTeam' => $user->currentTeam = $team]);

View File

@ -38,8 +38,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\CheckForcePasswordReset::class,
\App\Http\Middleware\IsSubscriptionValid::class,
\App\Http\Middleware\IsBoardingFlow::class,
\App\Http\Middleware\DecideWhatToDoWithUser::class,
],

View File

@ -21,11 +21,13 @@ public function mount()
public function check_status()
{
dispatch(new ContainerStatusJob($this->application->destination->server));
$this->application->refresh();
$this->application->previews->each(function ($preview) {
$preview->refresh();
});
if ($this->application->destination->server->isFunctional()) {
dispatch(new ContainerStatusJob($this->application->destination->server));
$this->application->refresh();
$this->application->previews->each(function ($preview) {
$preview->refresh();
});
}
}
public function force_deploy_without_cache()

View File

@ -31,7 +31,9 @@ public function delete()
$destination = $this->resource->destination->getMorphClass()::where('id', $this->resource->destination->id)->first();
$server = $destination->server;
}
instant_remote_process(["docker rm -f {$this->resource->uuid}"], $server);
if ($this->resource->destination->server->isFunctional()) {
instant_remote_process(["docker rm -f {$this->resource->uuid}"], $server);
}
}
$this->resource->delete();
return redirect()->route('project.resources', [

View File

@ -15,7 +15,7 @@ public function mount()
{
$this->parameters = get_route_parameters();
try {
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
}

View File

@ -32,34 +32,18 @@ public function setPrivateKey($newPrivateKeyId)
}
}
public function checkConnection($install = false)
public function checkConnection()
{
try {
$uptime = $this->server->validateConnection();
if ($uptime) {
$install && $this->emit('success', 'Server is reachable.');
$this->emit('success', 'Server is reachable.');
} else {
$install && $this->emit('error', 'Server is not reachable. Please check your connection and private key configuration.');
return;
}
$dockerInstalled = $this->server->validateDockerEngine();
if ($dockerInstalled) {
$install && $this->emit('success', 'Docker Engine is installed.<br> Checking version.');
} else {
$install && $this->installDocker();
return;
}
$dockerVersion = $this->server->validateDockerEngineVersion();
if ($dockerVersion) {
$install && $this->emit('success', 'Docker Engine version is 23+.');
} else {
$install && $this->installDocker();
$this->emit('error', 'Server is not reachable. Please check your connection and private key configuration.');
return;
}
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->emit('proxyStatusUpdated');
}
}

View File

@ -5,10 +5,11 @@
use App\Actions\Server\UpdateCoolify;
use App\Models\InstanceSettings;
use Livewire\Component;
use Masmerise\Toaster\Toaster;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
class Upgrade extends Component
{
use WithRateLimiting;
public bool $showProgress = false;
public bool $isUpgradeAvailable = false;
public string $latestVersion = '';
@ -31,6 +32,7 @@ public function checkUpdate()
public function upgrade()
{
try {
$this->rateLimit(1, 30);
if ($this->showProgress) {
return;
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
class VerifyEmail extends Component
{
use WithRateLimiting;
public function again() {
try {
$this->rateLimit(1, 300);
auth()->user()->sendVerificationEmail();
$this->emit('success', 'Email verification link sent!');
} catch(\Exception $e) {
ray($e);
return handleError($e,$this);
}
}
public function render()
{
return view('livewire.verify-email');
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Str;
class DecideWhatToDoWithUser
{
public function handle(Request $request, Closure $next): Response
{
if (!auth()->user() || !isCloud() || isInstanceAdmin()) {
return $next($request);
}
if (!auth()->user()->hasVerifiedEmail()) {
if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) {
return $next($request);
}
return redirect('/verify');
}
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
if (Str::startsWith($request->path(), 'invitations')) {
return $next($request);
}
return redirect('subscription');
}
}
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
if (Str::startsWith($request->path(), 'invitations')) {
return $next($request);
}
return redirect('boarding');
}
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
return redirect('/');
}
if (isSubscriptionActive() && $request->path() === 'subscription') {
return redirect('/');
}
return $next($request);
}
}

View File

@ -46,32 +46,43 @@ public function handle()
try {
ray("checking server status for {$this->server->name}");
// ray()->clearAll();
$serverUptimeCheckNumber = 0;
$serverUptimeCheckNumber = $this->server->unreachable_count;
$serverUptimeCheckNumberMax = 3;
while (true) {
ray('checking # ' . $serverUptimeCheckNumber);
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
send_internal_notification('Server unreachable: ' . $this->server->name);
if ($this->server->unreachable_email_sent === false) {
ray('Server unreachable, sending notification...');
$this->server->team->notify(new Unreachable($this->server));
}
$this->server->settings()->update([
'is_reachable' => false,
]);
ray('checking # ' . $serverUptimeCheckNumber);
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
if ($this->server->unreachable_email_sent === false) {
ray('Server unreachable, sending notification...');
// $this->server->team->notify(new Unreachable($this->server));
$this->server->update(['unreachable_email_sent' => true]);
return;
}
$result = $this->server->validateConnection();
if ($result) {
break;
}
$serverUptimeCheckNumber++;
sleep(5);
$this->server->settings()->update([
'is_reachable' => false,
]);
return;
}
$result = $this->server->validateConnection();
if ($result) {
$this->server->settings()->update([
'is_reachable' => true,
]);
$this->server->update([
'unreachable_count' => 0,
]);
} else {
$serverUptimeCheckNumber++;
$this->server->settings()->update([
'is_reachable' => false,
]);
$this->server->update([
'unreachable_count' => $serverUptimeCheckNumber,
]);
return;
}
if (data_get($this->server, 'unreachable_email_sent') === true) {
ray('Server is reachable again, sending notification...');
$this->server->team->notify(new Revived($this->server));
// $this->server->team->notify(new Revived($this->server));
$this->server->update(['unreachable_email_sent' => false]);
}
if (
@ -83,7 +94,7 @@ public function handle()
'is_usable' => true
]);
}
$this->server->validateDockerEngine(true);
// $this->server->validateDockerEngine(true);
$containers = instant_remote_process(["docker container ls -q"], $this->server);
if (!$containers) {
return;

View File

@ -33,11 +33,13 @@ protected static function booted()
});
static::deleting(function ($application) {
// Stop Container
instant_remote_process(
["docker rm -f {$application->uuid}"],
$application->destination->server,
false
);
if ($application->destination->server->isFunctional()) {
instant_remote_process(
["docker rm -f {$application->uuid}"],
$application->destination->server,
false
);
}
$application->settings()->delete();
$storages = $application->persistentStorages()->get();
foreach ($storages as $storage) {

View File

@ -33,7 +33,7 @@ public function type()
}
if (isStripe()) {
if (!$this->stripe_plan_id) {
return 'zero';
return 'zero';
}
$subscription = Subscription::where('id', $this->id)->first();
if (!$subscription) {

View File

@ -6,8 +6,12 @@
use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\URL;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Sanctum\HasApiTokens;
@ -54,6 +58,23 @@ public function getRecepients($notification)
return $this->email;
}
public function sendVerificationEmail()
{
$mail = new MailMessage();
$url = Url::temporarySignedRoute(
'verify.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'id' => $this->getKey(),
'hash' => sha1($this->getEmailForVerification()),
]
);
$mail->view('emails.email-verification', [
'url' => $url,
]);
$mail->subject('Coolify Cloud: Verify your email.');
send_user_an_email($mail, $this->email);
}
public function sendPasswordResetNotification($token): void
{
$this->notify(new TransactionalEmailsResetPassword($token));
@ -61,7 +82,7 @@ public function sendPasswordResetNotification($token): void
public function isAdmin()
{
return data_get($this->pivot,'role') === 'admin' || data_get($this->pivot,'role') === 'owner';
return data_get($this->pivot, 'role') === 'admin' || data_get($this->pivot, 'role') === 'owner';
}
public function isAdminFromSession()
@ -79,7 +100,7 @@ public function isAdminFromSession()
return true;
}
$team = $teams->where('id', session('currentTeam')->id)->first();
$role = data_get($team,'pivot.role');
$role = data_get($team, 'pivot.role');
return $role === 'admin' || $role === 'owner';
}
@ -96,7 +117,7 @@ public function isInstanceAdmin()
public function currentTeam()
{
return Cache::remember('team:' . auth()->user()->id, 3600, function() {
return Cache::remember('team:' . auth()->user()->id, 3600, function () {
return Team::find(session('currentTeam')->id);
});
}

View File

@ -180,55 +180,55 @@ function refresh_server_connection(PrivateKey $private_key)
}
}
function validateServer(Server $server, bool $throwError = false)
{
try {
$uptime = instant_remote_process(['uptime'], $server, $throwError);
if (!$uptime) {
$server->settings->is_reachable = false;
$server->team->notify(new Unreachable($server));
$server->unreachable_email_sent = true;
$server->save();
return [
"uptime" => null,
"dockerVersion" => null,
];
}
$server->settings->is_reachable = true;
instant_remote_process(["docker ps"], $server, $throwError);
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
if (!$dockerVersion) {
$dockerVersion = null;
return [
"uptime" => $uptime,
"dockerVersion" => null,
];
}
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
if (is_null($dockerVersion)) {
$server->settings->is_usable = false;
} else {
$server->settings->is_usable = true;
if (data_get($server, 'unreachable_email_sent') === true) {
$server->team->notify(new Revived($server));
$server->unreachable_email_sent = false;
$server->save();
}
}
return [
"uptime" => $uptime,
"dockerVersion" => $dockerVersion,
];
} catch (\Throwable $e) {
$server->settings->is_reachable = false;
$server->settings->is_usable = false;
throw $e;
} finally {
if (data_get($server, 'settings')) {
$server->settings->save();
}
}
}
// function validateServer(Server $server, bool $throwError = false)
// {
// try {
// $uptime = instant_remote_process(['uptime'], $server, $throwError);
// if (!$uptime) {
// $server->settings->is_reachable = false;
// $server->team->notify(new Unreachable($server));
// $server->unreachable_email_sent = true;
// $server->save();
// return [
// "uptime" => null,
// "dockerVersion" => null,
// ];
// }
// $server->settings->is_reachable = true;
// instant_remote_process(["docker ps"], $server, $throwError);
// $dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
// if (!$dockerVersion) {
// $dockerVersion = null;
// return [
// "uptime" => $uptime,
// "dockerVersion" => null,
// ];
// }
// $dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
// if (is_null($dockerVersion)) {
// $server->settings->is_usable = false;
// } else {
// $server->settings->is_usable = true;
// if (data_get($server, 'unreachable_email_sent') === true) {
// $server->team->notify(new Revived($server));
// $server->unreachable_email_sent = false;
// $server->save();
// }
// }
// return [
// "uptime" => $uptime,
// "dockerVersion" => $dockerVersion,
// ];
// } catch (\Throwable $e) {
// $server->settings->is_reachable = false;
// $server->settings->is_usable = false;
// throw $e;
// } finally {
// if (data_get($server, 'settings')) {
// $server->settings->save();
// }
// }
// }
function checkRequiredCommands(Server $server)
{

View File

@ -122,14 +122,14 @@ function allowedPathsForUnsubscribedAccounts()
return [
'subscription',
'login',
'register',
'logout',
'waitlist',
'force-password-reset',
'logout',
'livewire/message/force-password-reset',
'livewire/message/check-license',
'livewire/message/switch-team',
'livewire/message/subscription.pricing-plans'
'livewire/message/subscription.pricing-plans',
'livewire/message/help'
];
}
function allowedPathsForBoardingAccounts()
@ -141,3 +141,11 @@ function allowedPathsForBoardingAccounts()
'livewire/message/activity-monitor'
];
}
function allowedPathsForInvalidAccounts() {
return [
'logout',
'verify',
'livewire/message/verify-email',
'livewire/message/help'
];
}

136
composer.lock generated
View File

@ -62,16 +62,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.283.0",
"version": "3.283.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "5084c03431ecda0003e35d7fc7a12eeca4242685"
"reference": "6616677d76e39af28138512740199d38a461859f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5084c03431ecda0003e35d7fc7a12eeca4242685",
"reference": "5084c03431ecda0003e35d7fc7a12eeca4242685",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6616677d76e39af28138512740199d38a461859f",
"reference": "6616677d76e39af28138512740199d38a461859f",
"shasum": ""
},
"require": {
@ -151,9 +151,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.283.0"
"source": "https://github.com/aws/aws-sdk-php/tree/3.283.2"
},
"time": "2023-10-04T18:08:32+00:00"
"time": "2023-10-06T18:09:54+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -1083,16 +1083,16 @@
},
{
"name": "egulias/email-validator",
"version": "4.0.1",
"version": "4.0.2",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
"reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff"
"reference": "ebaaf5be6c0286928352e054f2d5125608e5405e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff",
"reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e",
"reference": "ebaaf5be6c0286928352e054f2d5125608e5405e",
"shasum": ""
},
"require": {
@ -1101,8 +1101,8 @@
"symfony/polyfill-intl-idn": "^1.26"
},
"require-dev": {
"phpunit/phpunit": "^9.5.27",
"vimeo/psalm": "^4.30"
"phpunit/phpunit": "^10.2",
"vimeo/psalm": "^5.12"
},
"suggest": {
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
@ -1138,7 +1138,7 @@
],
"support": {
"issues": "https://github.com/egulias/EmailValidator/issues",
"source": "https://github.com/egulias/EmailValidator/tree/4.0.1"
"source": "https://github.com/egulias/EmailValidator/tree/4.0.2"
},
"funding": [
{
@ -1146,7 +1146,7 @@
"type": "github"
}
],
"time": "2023-01-14T14:17:03+00:00"
"time": "2023-10-06T06:47:41+00:00"
},
{
"name": "fruitcake/php-cors",
@ -2724,16 +2724,16 @@
},
{
"name": "league/flysystem",
"version": "3.16.0",
"version": "3.17.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729"
"reference": "bd4c9b26849d82364119c68429541f1631fba94b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fdf372ca6b63c6e281b1c01a624349ccb757729",
"reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/bd4c9b26849d82364119c68429541f1631fba94b",
"reference": "bd4c9b26849d82364119c68429541f1631fba94b",
"shasum": ""
},
"require": {
@ -2751,8 +2751,8 @@
"symfony/http-client": "<5.2"
},
"require-dev": {
"async-aws/s3": "^1.5",
"async-aws/simple-s3": "^1.1",
"async-aws/s3": "^1.5 || ^2.0",
"async-aws/simple-s3": "^1.1 || ^2.0",
"aws/aws-sdk-php": "^3.220.0",
"composer/semver": "^3.0",
"ext-fileinfo": "*",
@ -2798,7 +2798,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/3.16.0"
"source": "https://github.com/thephpleague/flysystem/tree/3.17.0"
},
"funding": [
{
@ -2810,7 +2810,7 @@
"type": "github"
}
],
"time": "2023-09-07T19:22:17+00:00"
"time": "2023-10-05T20:15:05+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
@ -3557,16 +3557,16 @@
},
{
"name": "nette/schema",
"version": "v1.2.4",
"version": "v1.2.5",
"source": {
"type": "git",
"url": "https://github.com/nette/schema.git",
"reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab"
"reference": "0462f0166e823aad657c9224d0f849ecac1ba10a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/schema/zipball/c9ff517a53903b3d4e29ec547fb20feecb05b8ab",
"reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab",
"url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a",
"reference": "0462f0166e823aad657c9224d0f849ecac1ba10a",
"shasum": ""
},
"require": {
@ -3613,9 +3613,9 @@
],
"support": {
"issues": "https://github.com/nette/schema/issues",
"source": "https://github.com/nette/schema/tree/v1.2.4"
"source": "https://github.com/nette/schema/tree/v1.2.5"
},
"time": "2023-08-05T18:56:25+00:00"
"time": "2023-10-05T20:37:59+00:00"
},
{
"name": "nette/utils",
@ -6757,16 +6757,16 @@
},
{
"name": "stripe/stripe-php",
"version": "v12.5.0",
"version": "v12.6.0",
"source": {
"type": "git",
"url": "https://github.com/stripe/stripe-php.git",
"reference": "a4249b4a90437844f6c35e8701f8c68acd206f56"
"reference": "e0c15e4cbf252e708b937482bb1a50fa7e01bc9d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/a4249b4a90437844f6c35e8701f8c68acd206f56",
"reference": "a4249b4a90437844f6c35e8701f8c68acd206f56",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/e0c15e4cbf252e708b937482bb1a50fa7e01bc9d",
"reference": "e0c15e4cbf252e708b937482bb1a50fa7e01bc9d",
"shasum": ""
},
"require": {
@ -6811,9 +6811,9 @@
],
"support": {
"issues": "https://github.com/stripe/stripe-php/issues",
"source": "https://github.com/stripe/stripe-php/tree/v12.5.0"
"source": "https://github.com/stripe/stripe-php/tree/v12.6.0"
},
"time": "2023-09-28T23:06:27+00:00"
"time": "2023-10-05T18:01:43+00:00"
},
{
"name": "symfony/console",
@ -10258,16 +10258,16 @@
"packages-dev": [
{
"name": "brianium/paratest",
"version": "v7.2.8",
"version": "v7.2.9",
"source": {
"type": "git",
"url": "https://github.com/paratestphp/paratest.git",
"reference": "882b02d197328138686bb06ce7d8cbb98fc0a16c"
"reference": "1f9e41c0779be4540654d92a9314016713f5e62c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/882b02d197328138686bb06ce7d8cbb98fc0a16c",
"reference": "882b02d197328138686bb06ce7d8cbb98fc0a16c",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/1f9e41c0779be4540654d92a9314016713f5e62c",
"reference": "1f9e41c0779be4540654d92a9314016713f5e62c",
"shasum": ""
},
"require": {
@ -10275,13 +10275,13 @@
"ext-pcre": "*",
"ext-reflection": "*",
"ext-simplexml": "*",
"fidry/cpu-core-counter": "^0.4.1 || ^0.5.1",
"fidry/cpu-core-counter": "^0.5.1",
"jean85/pretty-package-versions": "^2.0.5",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"phpunit/php-code-coverage": "^10.1.3",
"phpunit/php-file-iterator": "^4.0.2",
"phpunit/php-code-coverage": "^10.1.7",
"phpunit/php-file-iterator": "^4.1.0",
"phpunit/php-timer": "^6.0",
"phpunit/phpunit": "^10.3.2",
"phpunit/phpunit": "^10.4.0",
"sebastian/environment": "^6.0.1",
"symfony/console": "^6.3.4",
"symfony/process": "^6.3.4"
@ -10290,8 +10290,8 @@
"doctrine/coding-standard": "^12.0.0",
"ext-pcov": "*",
"ext-posix": "*",
"infection/infection": "^0.27.0",
"phpstan/phpstan": "^1.10.32",
"infection/infection": "^0.27.3",
"phpstan/phpstan": "^1.10.37",
"phpstan/phpstan-deprecation-rules": "^1.1.4",
"phpstan/phpstan-phpunit": "^1.3.14",
"phpstan/phpstan-strict-rules": "^1.5.1",
@ -10337,7 +10337,7 @@
],
"support": {
"issues": "https://github.com/paratestphp/paratest/issues",
"source": "https://github.com/paratestphp/paratest/tree/v7.2.8"
"source": "https://github.com/paratestphp/paratest/tree/v7.2.9"
},
"funding": [
{
@ -10349,7 +10349,7 @@
"type": "paypal"
}
],
"time": "2023-10-04T13:38:04+00:00"
"time": "2023-10-06T07:53:04+00:00"
},
{
"name": "fakerphp/faker",
@ -10983,35 +10983,35 @@
},
{
"name": "pestphp/pest",
"version": "v2.20.0",
"version": "v2.21.0",
"source": {
"type": "git",
"url": "https://github.com/pestphp/pest.git",
"reference": "a8b785f69e44ae3f902cbf08fe6b79359ba46945"
"reference": "2ffafd445d42c8b7b7e1874bde1c29945767a49d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pestphp/pest/zipball/a8b785f69e44ae3f902cbf08fe6b79359ba46945",
"reference": "a8b785f69e44ae3f902cbf08fe6b79359ba46945",
"url": "https://api.github.com/repos/pestphp/pest/zipball/2ffafd445d42c8b7b7e1874bde1c29945767a49d",
"reference": "2ffafd445d42c8b7b7e1874bde1c29945767a49d",
"shasum": ""
},
"require": {
"brianium/paratest": "^7.2.7",
"brianium/paratest": "^7.2.9",
"nunomaduro/collision": "^7.9.0",
"nunomaduro/termwind": "^1.15.1",
"pestphp/pest-plugin": "^2.1.1",
"pestphp/pest-plugin-arch": "^2.3.3",
"php": "^8.1.0",
"phpunit/phpunit": "^10.3.5"
"phpunit/phpunit": "^10.4.0"
},
"conflict": {
"phpunit/phpunit": ">10.3.5",
"phpunit/phpunit": ">10.4.0",
"sebastian/exporter": "<5.1.0",
"webmozart/assert": "<1.11.0"
},
"require-dev": {
"pestphp/pest-dev-tools": "^2.16.0",
"pestphp/pest-plugin-type-coverage": "^2.2.0",
"pestphp/pest-plugin-type-coverage": "^2.4.0",
"symfony/process": "^6.3.4"
},
"bin": [
@ -11070,7 +11070,7 @@
],
"support": {
"issues": "https://github.com/pestphp/pest/issues",
"source": "https://github.com/pestphp/pest/tree/v2.20.0"
"source": "https://github.com/pestphp/pest/tree/v2.21.0"
},
"funding": [
{
@ -11082,7 +11082,7 @@
"type": "github"
}
],
"time": "2023-09-29T18:05:52+00:00"
"time": "2023-10-06T12:33:39+00:00"
},
{
"name": "pestphp/pest-plugin",
@ -11454,16 +11454,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.37",
"version": "1.10.38",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e"
"reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/058ba07e92f744d4dcf6061ae75283d0c6456f2e",
"reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5302bb402c57f00fb3c2c015bac86e0827e4b691",
"reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691",
"shasum": ""
},
"require": {
@ -11512,7 +11512,7 @@
"type": "tidelift"
}
],
"time": "2023-10-02T16:18:37+00:00"
"time": "2023-10-06T14:19:14+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -11837,16 +11837,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.3.5",
"version": "10.4.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503"
"reference": "9784e877e3700de37475545bdbdce8383ff53d25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503",
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9784e877e3700de37475545bdbdce8383ff53d25",
"reference": "9784e877e3700de37475545bdbdce8383ff53d25",
"shasum": ""
},
"require": {
@ -11886,7 +11886,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "10.3-dev"
"dev-main": "10.4-dev"
}
},
"autoload": {
@ -11918,7 +11918,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.0"
},
"funding": [
{
@ -11934,7 +11934,7 @@
"type": "tidelift"
}
],
"time": "2023-09-19T05:42:37+00:00"
"time": "2023-10-06T03:41:22+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -1,6 +1,7 @@
<?php
return [
'docs' => 'https://coolify.io/contact',
'self_hosted' => env('SELF_HOSTED', true),
'waitlist' => env('WAITLIST', false),
'license_url' => 'https://licenses.coollabs.io',

View File

@ -7,7 +7,7 @@
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.70',
'release' => '4.0.0-beta.71',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.70';
return '4.0.0-beta.71';

View File

@ -0,0 +1,28 @@
<?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('servers', function (Blueprint $table) {
$table->integer('unreachable_count')->default(0);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('servers', function (Blueprint $table) {
$table->dropColumn('unreachable_count');
});
}
};

58
package-lock.json generated
View File

@ -6,18 +6,18 @@
"": {
"dependencies": {
"@tailwindcss/typography": "0.5.10",
"alpinejs": "3.13.0",
"daisyui": "3.7.7",
"alpinejs": "3.13.1",
"daisyui": "3.9.2",
"tailwindcss-scrollbar": "0.1.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "4.3.4",
"@vitejs/plugin-vue": "4.4.0",
"autoprefixer": "10.4.16",
"axios": "1.5.0",
"laravel-vite-plugin": "0.8.0",
"postcss": "8.4.30",
"axios": "1.5.1",
"laravel-vite-plugin": "0.8.1",
"postcss": "8.4.31",
"tailwindcss": "3.3.3",
"vite": "4.4.9",
"vite": "4.4.11",
"vue": "3.3.4"
}
},
@ -503,9 +503,9 @@
}
},
"node_modules/@vitejs/plugin-vue": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
"integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz",
"integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==",
"dev": true,
"engines": {
"node": "^14.18.0 || >=16.0.0"
@ -683,9 +683,9 @@
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
},
"node_modules/alpinejs": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.0.tgz",
"integrity": "sha512-7FYR1Yz3evIjlJD1mZ3SYWSw+jlOmQGeQ1QiSufSQ6J84XMQFkzxm6OobiZ928SfqhGdoIp2SsABNsS4rXMMJw==",
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.1.tgz",
"integrity": "sha512-/LZ7mumW02V7AV5xTTftJFHS0I3KOXLl7tHm4xpxXAV+HJ/zjTT0n8MU7RZ6UoGPhmO/i+KEhQojaH/0RsH5tg==",
"dependencies": {
"@vue/reactivity": "~3.1.1"
}
@ -756,9 +756,9 @@
}
},
"node_modules/axios": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
"integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
"integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.15.0",
@ -953,15 +953,15 @@
"dev": true
},
"node_modules/daisyui": {
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.7.7.tgz",
"integrity": "sha512-2/nFdW/6R9MMnR8tTm07jPVyPaZwpUSkVsFAADb7Oq8N2Ynbls57laDdNqxTCUmn0QvcZi01TKl8zQbAwRfw1w==",
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.2.tgz",
"integrity": "sha512-yJZ1QjHUaL+r9BkquTdzNHb7KIgAJVFh0zbOXql2Wu0r7zx5qZNLxclhjN0WLoIpY+o2h/8lqXg7ijj8oTigOw==",
"dependencies": {
"colord": "^2.9",
"css-selector-tokenizer": "^0.8",
"postcss": "^8",
"postcss-js": "^4",
"tailwindcss": "^3"
"tailwindcss": "^3.1"
},
"engines": {
"node": ">=16.9.0"
@ -1289,9 +1289,9 @@
}
},
"node_modules/laravel-vite-plugin": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.0.tgz",
"integrity": "sha512-6VjLI+azBpeK6rWBiKcb/En5GnTdYpL0U4zS8gXYvb2/VSq4mlau5H3NWpSktUDBMM1b97LLgICx5zevi8IY0w==",
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.1.tgz",
"integrity": "sha512-fxzUDjOA37kOsYq8dP+3oPIlw8/kJVXwu0hOXLun82R1LpV02shGeWGYKx2lbpKffL5I0sfPPjfqbYxuqBluAA==",
"dev": true,
"dependencies": {
"picocolors": "^1.0.0",
@ -1516,9 +1516,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.30",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [
{
"type": "opencollective",
@ -1920,9 +1920,9 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/vite": {
"version": "4.4.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
"version": "4.4.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
"integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
"dev": true,
"dependencies": {
"esbuild": "^0.18.10",

View File

@ -6,19 +6,19 @@
"build": "vite build"
},
"devDependencies": {
"@vitejs/plugin-vue": "4.3.4",
"@vitejs/plugin-vue": "4.4.0",
"autoprefixer": "10.4.16",
"axios": "1.5.0",
"laravel-vite-plugin": "0.8.0",
"postcss": "8.4.30",
"axios": "1.5.1",
"laravel-vite-plugin": "0.8.1",
"postcss": "8.4.31",
"tailwindcss": "3.3.3",
"vite": "4.4.9",
"vite": "4.4.11",
"vue": "3.3.4"
},
"dependencies": {
"@tailwindcss/typography": "0.5.10",
"alpinejs": "3.13.0",
"daisyui": "3.7.7",
"alpinejs": "3.13.1",
"daisyui": "3.9.2",
"tailwindcss-scrollbar": "0.1.0"
}
}

View File

@ -9,12 +9,12 @@
@if ($is_registration_enabled)
@if (config('coolify.waitlist'))
<a href="/waitlist"
class="text-xs normal-case hover:no-underline btn btn-sm bg-coollabs-gradient">
class="text-xs text-center text-white normal-case bg-transparent border-none rounded no-animation hover:no-underline btn btn-sm bg-coollabs-gradient">
Join the waitlist
</a>
@else
<a href="/register"
class="text-xs normal-case hover:no-underline btn btn-sm bg-coollabs-gradient">
class="text-xs text-center text-white normal-case bg-transparent border-none rounded no-animation hover:no-underline btn btn-sm bg-coollabs-gradient">
{{ __('auth.register_now') }}
</a>
@endif

View File

@ -0,0 +1,12 @@
<x-layout-subscription>
<div class="min-h-screen hero">
<div class="min-w-fit">
<h1> Verification Email Sent </h1>
<div class="flex justify-center gap-2 text-center">
<br>To activate your account, please open the email and follow the
instructions.
</div>
<livewire:verify-email />
</div>
</div>
</x-layout-subscription>

View File

@ -256,7 +256,7 @@ class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap
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>
href="{{ config('coolify.docs') }}">Contact Us</a>
</x-forms.button>
</div>
</div>

View File

@ -0,0 +1,3 @@
<x-emails.layout>
Verify your email [here]({{ $url }}).
</x-emails.layout>

View File

@ -10,7 +10,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="https://docs.coollabs.io/contact.html">Contact
<a target="_blank" class="text-xs" href="{{ config('coolify.docs') }}">Contact
support
<x-external-link />
</a>

View File

@ -10,7 +10,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="https://docs.coollabs.io/contact.html">Contact
<a target="_blank" class="text-xs" href="{{ config('coolify.docs') }}">Contact
support
<x-external-link />
</a>

View File

@ -11,7 +11,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="https://docs.coollabs.io/contact.html">Contact
<a target="_blank" class="text-xs" href="{{ config('coolify.docs') }}">Contact
support
<x-external-link />
</a>

View File

@ -11,7 +11,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a href="https://docs.coollabs.io/contact.html" class="font-semibold text-white ">Contact
<a href="{{ config('coolify.docs') }}" class="font-semibold text-white ">Contact
support
<span aria-hidden="true">&rarr;</span></a>
</div>

View File

@ -10,7 +10,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a href="https://docs.coollabs.io/contact.html" class="font-semibold text-white ">Contact
<a href="{{ config('coolify.docs') }}" class="font-semibold text-white ">Contact
support
<span aria-hidden="true">&rarr;</span></a>
</div>

View File

@ -14,7 +14,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a href="https://docs.coollabs.io/contact.html" class="font-semibold text-white">Contact
<a href="{{ config('coolify.docs') }}" class="font-semibold text-white">Contact
support
<span aria-hidden="true">&rarr;</span></a>
</div>

View File

@ -8,7 +8,7 @@
patience.
</p>
<div class="flex items-center justify-center mt-10 gap-x-6">
<a href="https://docs.coollabs.io/contact.html" class="font-semibold text-white ">Contact
<a href="{{ config('coolify.docs') }}" class="font-semibold text-white ">Contact
support
<span aria-hidden="true">&rarr;</span></a>
</div>

View File

@ -16,7 +16,7 @@
again</x-forms.button></a>
@endif
<div>To update your subscription (upgrade / downgrade), please <a class="text-white underline"
href="https://docs.coollabs.io/contact" target="_blank">contact us.</a></div>
href="{{ config('coolify.docs') }}" target="_blank">contact us.</a></div>
</div>
@endif
@if (subscriptionProvider() === 'lemon')
@ -25,7 +25,7 @@
@if (currentTeam()->subscription->lemon_status === 'cancelled')
<div class="pb-4">Subscriptions ends at: {{ getRenewDate() }}</div>
<div class="py-4">If you would like to change the subscription to a lower/higher plan, <a
class="text-white underline" href="https://docs.coollabs.io/contact" target="_blank">please
class="text-white underline" href="{{ config('coolify.docs') }}" target="_blank">please
contact
us.</a></div>
@else

View File

@ -23,12 +23,12 @@ class="w-full h-10 buyme" wire:click="subscribeStripe('pro-monthly')">
</x-slot:pro>
<x-slot:ultimate>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-ultimate"
class="w-full h-10 buyme"><a class="text-white hover:no-underline" href="https://docs.coollabs.io/contact" target="_blank">
class="w-full h-10 buyme"><a class="text-white hover:no-underline" href="{{ config('coolify.docs') }}" target="_blank">
Contact Us</a>
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-ultimate"
class="w-full h-10 buyme"><a class="text-white hover:no-underline" href="https://docs.coollabs.io/contact" target="_blank">
class="w-full h-10 buyme"><a class="text-white hover:no-underline" href="{{ config('coolify.docs') }}" target="_blank">
Contact Us</a>
</x-forms.button>
</x-slot:ultimate>

View File

@ -0,0 +1,3 @@
<div class="pt-4">
<x-forms.button wire:click="again">Send Verification Email Again</x-forms.button>
</div>

View File

@ -26,6 +26,7 @@
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Route;
@ -61,7 +62,19 @@
}
return response()->json(['message' => 'Transactional emails are not active'], 400);
})->name('password.forgot');
Route::get('/waitlist', WaitlistIndex::class)->name('waitlist.index');
Route::get('/verify', function () {
return view('auth.verify-email');
})->middleware('auth')->name('verify.email');
Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) {
$request->fulfill();
return redirect('/');
})->middleware(['auth'])->name('verify.verify');
Route::middleware(['throttle:login'])->group(function () {
Route::get('/auth/link', [Controller::class, 'link'])->name('auth.link');
});
@ -74,7 +87,7 @@
Route::get('/environment/new', [MagicController::class, 'newEnvironment']);
});
Route::middleware(['auth'])->group(function () {
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/projects', [ProjectController::class, 'all'])->name('projects');
Route::get('/project/{project_uuid}/edit', [ProjectController::class, 'edit'])->name('project.edit');
Route::get('/project/{project_uuid}', [ProjectController::class, 'show'])->name('project.show');
@ -114,7 +127,7 @@
});
Route::middleware(['auth'])->group(function () {
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/', Dashboard::class)->name('dashboard');
Route::get('/boarding', BoardingIndex::class)->name('boarding');
Route::middleware(['throttle:force-password-reset'])->group(function () {