diff --git a/app/Http/Middleware/DecideWhatToDoWithUser.php b/app/Http/Middleware/DecideWhatToDoWithUser.php index 30780910b..f4adaaa9b 100644 --- a/app/Http/Middleware/DecideWhatToDoWithUser.php +++ b/app/Http/Middleware/DecideWhatToDoWithUser.php @@ -44,7 +44,7 @@ class DecideWhatToDoWithUser if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') { return redirect(RouteServiceProvider::HOME); } - if (isSubscriptionActive() && $request->path() === 'subscription') { + if (isSubscriptionActive() && $request->routeIs('subscription.index')) { return redirect(RouteServiceProvider::HOME); } return $next($request); diff --git a/app/Livewire/Subscription/Actions.php b/app/Livewire/Subscription/Actions.php index bd4dbd69a..3050e38ab 100644 --- a/app/Livewire/Subscription/Actions.php +++ b/app/Livewire/Subscription/Actions.php @@ -7,6 +7,13 @@ use Livewire\Component; class Actions extends Component { + public $server_limits = 0; + public function mount() + { + $limits = currentTeam()->limits; + $this->server_limits = data_get($limits, 'serverLimit', 0); + + } public function cancel() { try { @@ -69,7 +76,8 @@ class Actions extends Component return handleError($e, $this); } } - public function stripeCustomerPortal() { + public function stripeCustomerPortal() + { $session = getStripeCustomerPortalSession(currentTeam()); redirect($session->url); } diff --git a/app/Livewire/Subscription/Index.php b/app/Livewire/Subscription/Index.php index afc3729c6..4f36750c7 100644 --- a/app/Livewire/Subscription/Index.php +++ b/app/Livewire/Subscription/Index.php @@ -10,14 +10,19 @@ class Index extends Component { public InstanceSettings $settings; public bool $alreadySubscribed = false; - public function mount() { + public function mount() + { if (!isCloud()) { return redirect(RouteServiceProvider::HOME); } + if (data_get(currentTeam(), 'subscription')) { + return redirect()->route('subscription.show'); + } $this->settings = InstanceSettings::get(); $this->alreadySubscribed = currentTeam()->subscription()->exists(); } - public function stripeCustomerPortal() { + public function stripeCustomerPortal() + { $session = getStripeCustomerPortalSession(currentTeam()); if (is_null($session)) { return; diff --git a/app/Livewire/Subscription/Show.php b/app/Livewire/Subscription/Show.php new file mode 100644 index 000000000..ad677ce53 --- /dev/null +++ b/app/Livewire/Subscription/Show.php @@ -0,0 +1,22 @@ +route('dashboard'); + } + if (!data_get(currentTeam(), 'subscription')) { + return redirect()->route('subscription.index'); + } + } + public function render() + { + return view('livewire.subscription.show'); + } +} diff --git a/app/Livewire/Team/Member.php b/app/Livewire/Team/Member.php index 7d02dbf66..0f0774898 100644 --- a/app/Livewire/Team/Member.php +++ b/app/Livewire/Team/Member.php @@ -16,6 +16,11 @@ class Member extends Component $this->dispatch('reloadWindow'); } + public function makeOwner() + { + $this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'owner']); + $this->dispatch('reloadWindow'); + } public function makeReadonly() { $this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'member']); @@ -26,7 +31,7 @@ class Member extends Component { $this->member->teams()->detach(currentTeam()); Cache::forget("team:{$this->member->id}"); - Cache::remember('team:' . $this->member->id, 3600, function() { + Cache::remember('team:' . $this->member->id, 3600, function () { return $this->member->teams()->first(); }); $this->dispatch('reloadWindow'); diff --git a/app/Models/Service.php b/app/Models/Service.php index 246e812c2..9fa175bae 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -6,7 +6,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; -use Illuminate\Support\Str; class Service extends BaseModel { @@ -28,47 +27,73 @@ class Service extends BaseModel { return $this->morphToMany(Tag::class, 'taggable'); } - public function status() { - $foundRunning = false; - $isDegraded = false; - $foundRestaring = false; + public function status() + { $applications = $this->applications; $databases = $this->databases; + + $complexStatus = null; + $complexHealth = null; + foreach ($applications as $application) { if ($application->exclude_from_status) { continue; } - if (Str::of($application->status)->startsWith('running')) { - $foundRunning = true; - } else if (Str::of($application->status)->startsWith('restarting')) { - $foundRestaring = true; + $status = str($application->status)->before('(')->trim(); + $health = str($application->status)->between('(', ')')->trim(); + if ($complexStatus === 'degraded') { + continue; + } + if ($status->startsWith('running')) { + if ($complexStatus === 'exited') { + $complexStatus = 'degraded'; + } else { + $complexStatus = 'running'; + } + } else if ($status->startsWith('restarting')) { + $complexStatus = 'degraded'; + } else if ($status->startsWith('exited')) { + $complexStatus = 'exited'; + } + if ($health->value() === 'healthy') { + if ($complexHealth === 'unhealthy') { + continue; + } + $complexHealth = 'healthy'; } else { - $isDegraded = true; + $complexHealth = 'unhealthy'; } } foreach ($databases as $database) { if ($database->exclude_from_status) { continue; } - if (Str::of($database->status)->startsWith('running')) { - $foundRunning = true; - } else if (Str::of($database->status)->startsWith('restarting')) { - $foundRestaring = true; + $status = str($database->status)->before('(')->trim(); + $health = str($database->status)->between('(', ')')->trim(); + if ($complexStatus === 'degraded') { + continue; + } + if ($status->startsWith('running')) { + if ($complexStatus === 'exited') { + $complexStatus = 'degraded'; + } else { + $complexStatus = 'running'; + } + } else if ($status->startsWith('restarting')) { + $complexStatus = 'degraded'; + } else if ($status->startsWith('exited')) { + $complexStatus = 'exited'; + } + if ($health->value() === 'healthy') { + if ($complexHealth === 'unhealthy') { + continue; + } + $complexHealth = 'healthy'; } else { - $isDegraded = true; + $complexHealth = 'unhealthy'; } } - if ($foundRestaring) { - return 'degraded'; - } - if ($foundRunning && !$isDegraded) { - return 'running'; - } else if ($foundRunning && $isDegraded) { - return 'degraded'; - } else if (!$foundRunning && !$isDegraded) { - return 'exited'; - } - return 'exited'; + return "{$complexStatus}:{$complexHealth}"; } public function extraFields() { @@ -414,7 +439,7 @@ class Service extends BaseModel public function documentation() { $services = getServiceTemplates(); - $service = data_get($services, Str::of($this->name)->beforeLast('-')->value, []); + $service = data_get($services, str($this->name)->beforeLast('-')->value, []); return data_get($service, 'documentation', config('constants.docs.base_url')); } public function applications() diff --git a/app/Models/Subscription.php b/app/Models/Subscription.php index cd35dc477..4b8c6d70e 100644 --- a/app/Models/Subscription.php +++ b/app/Models/Subscription.php @@ -30,8 +30,7 @@ class Subscription extends Model if (in_array($subscription, $ultimate)) { return 'ultimate'; } - } - if (isStripe()) { + } else if (isStripe()) { if (!$this->stripe_plan_id) { return 'zero'; } @@ -55,7 +54,7 @@ class Subscription extends Model }; })->first(); if ($stripePlanId) { - return Str::of($stripePlanId)->after('stripe_price_id_')->before('_')->lower(); + return str($stripePlanId)->after('stripe_price_id_')->before('_')->lower(); } } return 'zero'; diff --git a/app/Models/User.php b/app/Models/User.php index c099eb2b6..f8507a6b9 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -67,7 +67,7 @@ class User extends Authenticatable implements SendsEmail 'team_id' => session('currentTeam')->id ]); - return new NewAccessToken($token, $token->getKey().'|'.$plainTextToken); + return new NewAccessToken($token, $token->getKey() . '|' . $plainTextToken); } public function teams() { @@ -103,9 +103,13 @@ class User extends Authenticatable implements SendsEmail public function isAdmin() { - return data_get($this->pivot, 'role') === 'admin' || data_get($this->pivot, 'role') === 'owner'; + return $this->role() === 'admin' || $this->role() === 'owner'; } + public function isOwner() + { + return $this->role() === 'owner'; + } public function isAdminFromSession() { if (auth()->user()->id === 0) { @@ -155,6 +159,9 @@ class User extends Authenticatable implements SendsEmail public function role() { - return session('currentTeam')->pivot->role; + if (data_get($this, 'pivot')) { + return $this->pivot->role; + } + return auth()->user()->teams->where('id', currentTeam()->id)->first()->pivot->role; } } diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php index 47ea21e46..a26bed75b 100644 --- a/bootstrap/helpers/subscriptions.php +++ b/bootstrap/helpers/subscriptions.php @@ -109,7 +109,7 @@ function isPaddle() function getStripeCustomerPortalSession(Team $team) { Stripe::setApiKey(config('subscription.stripe_api_key')); - $return_url = route('team.index'); + $return_url = route('subscription.show'); $stripe_customer_id = data_get($team,'subscription.stripe_customer_id'); if (!$stripe_customer_id) { return null; @@ -123,7 +123,7 @@ function getStripeCustomerPortalSession(Team $team) function allowedPathsForUnsubscribedAccounts() { return [ - 'subscription', + 'subscription/new', 'login', 'logout', 'waitlist', diff --git a/config/sentry.php b/config/sentry.php index a183d2d97..00c5f77de 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // 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.223', + 'release' => '4.0.0-beta.224', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/subscription.php b/config/subscription.php index 677689ff1..96a9a8e1c 100644 --- a/config/subscription.php +++ b/config/subscription.php @@ -13,6 +13,12 @@ return [ 'stripe_price_id_ultimate_yearly' => env('STRIPE_PRICE_ID_ULTIMATE_YEARLY', null), 'stripe_excluded_plans' => env('STRIPE_EXCLUDED_PLANS', null), + 'stripe_price_id_basic_monthly_old' => env('STRIPE_PRICE_ID_BASIC_MONTHLY_OLD', null), + 'stripe_price_id_basic_yearly_old' => env('STRIPE_PRICE_ID_BASIC_YEARLY_OLD', null), + 'stripe_price_id_pro_monthly_old' => env('STRIPE_PRICE_ID_PRO_MONTHLY_OLD', null), + 'stripe_price_id_pro_yearly_old' => env('STRIPE_PRICE_ID_PRO_YEARLY_OLD', null), + 'stripe_price_id_ultimate_monthly_old' => env('STRIPE_PRICE_ID_ULTIMATE_MONTHLY_OLD', null), + 'stripe_price_id_ultimate_yearly_old' => env('STRIPE_PRICE_ID_ULTIMATE_YEARLY_OLD', null), // Paddle 'paddle_vendor_id' => env('PADDLE_VENDOR_ID', null), diff --git a/config/version.php b/config/version.php index 6ad89f693..c4e13f9de 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ . + --> + + \ No newline at end of file diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index 0713ca503..3bd1730be 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -44,9 +44,10 @@
@@ -131,11 +132,24 @@ - Teams @if (isCloud()) - / Subscription - @endif + Teams + @if (isCloud()) +
  • + + + + + Subscription + +
  • + @endif @if (isInstanceAdmin())
  • diff --git a/resources/views/components/pricing-plans.blade.php b/resources/views/components/pricing-plans.blade.php index 3773e2770..32e004c4f 100644 --- a/resources/views/components/pricing-plans.blade.php +++ b/resources/views/components/pricing-plans.blade.php @@ -34,7 +34,7 @@
    -
    + {{--

    Unlimited Trial Get Started @@ -42,8 +42,11 @@

    Start self-hosting without limits with our OSS version. Same features as the paid version, but you have to manage by yourself.

    -

    +
    --}} +
    +
    For the detailed list of features, please visit our landing page: coolify.io
    @@ -70,21 +73,18 @@ {{ $basic }} @endisset @endif -

    Start self-hosting in - the cloud - with a - single - server. +

    Begin hosting your own services in the + cloud.

      -
    • - + - 2 servers + Connect 2 servers
    • Scale your business or self-hosting environment. +

      Expand your business or set up your own hosting + environment.

        -
      • - + - 10 servers + Connect 10 servers
      • Deploy complex infrastructures and - manage them easily in one place.

        +

        Easily manage complex infrastructures in a + single location.

          -
        • - + - ? servers + Connect unlimited servers
        • @@ -254,7 +255,7 @@
    -
    + {{--
    Need official support for your self-hosted instance? @@ -263,9 +264,10 @@ Us
    -
    +
    --}} -
    Included in all plans
    + + {{--
    Included in all plans
    @@ -433,7 +435,7 @@
    * Some features are work in progress and will be available soon. -
    +
    --}}
    @isset($other) {{ $other }} diff --git a/resources/views/components/services/navbar.blade.php b/resources/views/components/services/navbar.blade.php index e5327935f..e593b79a8 100644 --- a/resources/views/components/services/navbar.blade.php +++ b/resources/views/components/services/navbar.blade.php @@ -1,11 +1,11 @@ @script - + @endscript diff --git a/resources/views/components/status/degraded.blade.php b/resources/views/components/status/degraded.blade.php index 81beedfa7..490ecc239 100644 --- a/resources/views/components/status/degraded.blade.php +++ b/resources/views/components/status/degraded.blade.php @@ -2,7 +2,12 @@ 'status' => 'Degraded', ]) -
    +
    -
    {{ Str::headline($status) }}
    +
    + {{ str($status)->before(':')->headline() }} +
    + @if (!str($status)->startsWith('Proxy') && !str($status)->contains('(')) +
    ({{ str($status)->after(':') }})
    + @endif
    diff --git a/resources/views/components/status/restarting.blade.php b/resources/views/components/status/restarting.blade.php index 50142de1a..d9c353d02 100644 --- a/resources/views/components/status/restarting.blade.php +++ b/resources/views/components/status/restarting.blade.php @@ -2,7 +2,7 @@ 'status' => 'Restarting', ]) -
    +
    {{ str($status)->before(':')->headline() }} diff --git a/resources/views/emails/before-trial-conversion.blade.php b/resources/views/emails/before-trial-conversion.blade.php index 8caee2dbc..09b9a54be 100644 --- a/resources/views/emails/before-trial-conversion.blade.php +++ b/resources/views/emails/before-trial-conversion.blade.php @@ -3,5 +3,5 @@ We would like to inform you that a {{ config('constants.limits.trial_period') }} You can try out Coolify, without payment information for free. If you like it, you can upgrade to a paid plan at any time. -[Click here](https://app.coolify.io/subscription) to start your trial. +[Click here](https://app.coolify.io/subscription/new) to start your trial. diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php index 7b5ea1417..e8ba873ea 100644 --- a/resources/views/livewire/dashboard.blade.php +++ b/resources/views/livewire/dashboard.blade.php @@ -5,7 +5,7 @@

    Dashboard

    Your self-hosted environment
    @if (request()->query->get('success')) -
    +
    @endif @if ($projects->count() === 0 && $servers->count() === 0) - No resources found. Add your first server / private key here. + No resources found. Add your first server & private key here or go to the boarding page. @endif @if ($projects->count() > 0)

    Projects

    - @endif - @if ($projects->count() === 1) -
    - @else -
    - @endif - @foreach ($projects as $project) -
    - @if (data_get($project, 'environments')->count() === 1) - -
    {{ $project->name }}
    -
    - {{ $project->description }}
    -
    + @if ($projects->count() === 1) +
    @else - -
    {{ $project->name }}
    -
    - {{ $project->description }}
    -
    - @endif -
    - - + Add Resource - - - - - - - - +
    + @endif + @foreach ($projects as $project) + -
    - @endforeach + @endforeach
    @if ($projects->count() > 0)

    Servers

    @@ -139,6 +138,7 @@
    No deployments running.
    @endforelse
    +@endif