diff --git a/app/Http/Controllers/ServerController.php b/app/Http/Controllers/ServerController.php new file mode 100644 index 000000000..1a5876c81 --- /dev/null +++ b/app/Http/Controllers/ServerController.php @@ -0,0 +1,24 @@ +user()->currentTeam()->servers->count(); + $subscription = auth()->user()->currentTeam()->subscription->type(); + $limits = config('constants.limits.server')[strtolower($subscription)]; + $limit_reached = true ?? $servers >= $limits[$subscription]; + return view('server.create', [ + 'limit_reached' => $limit_reached, + 'private_keys' => PrivateKey::ownedByCurrentTeam()->get(), + ]); + } +} diff --git a/app/Http/Livewire/Server/New/ByIp.php b/app/Http/Livewire/Server/New/ByIp.php index 233ea157d..c4f340413 100644 --- a/app/Http/Livewire/Server/New/ByIp.php +++ b/app/Http/Livewire/Server/New/ByIp.php @@ -8,6 +8,7 @@ class ByIp extends Component { public $private_keys; + public $limit_reached; public int|null $private_key_id = null; public $new_private_key_name; public $new_private_key_description; diff --git a/app/Http/Middleware/SubscriptionValid.php b/app/Http/Middleware/SubscriptionValid.php index c63c6d823..7d6cd5cfa 100644 --- a/app/Http/Middleware/SubscriptionValid.php +++ b/app/Http/Middleware/SubscriptionValid.php @@ -8,32 +8,38 @@ class SubscriptionValid { - public function handle(Request $request, Closure $next): Response { - if (auth()->user()) { - if (is_cloud() && !isSubscribed()) { - ray('SubscriptionValid Middleware'); + $is_instance_admin = auth()->user()?->isInstanceAdmin(); - $allowed_paths = [ - 'subscription', - 'login', - 'register', - 'logout', - 'livewire/message/check-license', - 'livewire/message/switch-team', - ]; - if (!in_array($request->path(), $allowed_paths)) { - return redirect('subscription'); - } else { - return $next($request); - } + if (!auth()->user() || !is_cloud()) { + if ($request->path() === 'subscription' && !$is_instance_admin) { + return redirect('/'); } else { - if ($request->path() === 'subscription' && !auth()->user()->isInstanceAdmin()) { - return redirect('/'); - } else { - return $next($request); - } + return $next($request); + } + } + if (is_subscription_active() && $request->path() === 'subscription' && !$is_instance_admin) { + return redirect('/'); + } + if (is_subscription_in_grace_period()) { + return $next($request); + } + if (!is_subscription_active() && !is_subscription_in_grace_period()) { + ray('SubscriptionValid Middleware'); + + $allowed_paths = [ + 'subscription', + 'login', + 'register', + 'logout', + 'livewire/message/check-license', + 'livewire/message/switch-team', + ]; + if (!in_array($request->path(), $allowed_paths)) { + return redirect('subscription'); + } else { + return $next($request); } } return $next($request); diff --git a/app/Models/Subscription.php b/app/Models/Subscription.php index 89c4264cb..4b78c37a9 100644 --- a/app/Models/Subscription.php +++ b/app/Models/Subscription.php @@ -12,4 +12,22 @@ public function team() { return $this->belongsTo(Team::class); } + public function type() + { + $basic = explode(',', config('coolify.lemon_squeezy_basic_plan_ids')); + $pro = explode(',', config('coolify.lemon_squeezy_pro_plan_ids')); + $ultimate = explode(',', config('coolify.lemon_squeezy_ultimate_plan_ids')); + + $subscription = $this->lemon_variant_id; + if (in_array($subscription, $basic)) { + return 'basic'; + } + if (in_array($subscription, $pro)) { + return 'pro'; + } + if (in_array($subscription, $ultimate)) { + return 'ultimate'; + } + return 'unknown'; + } } diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php index 64cf9b24c..d362591b7 100644 --- a/bootstrap/helpers/subscriptions.php +++ b/bootstrap/helpers/subscriptions.php @@ -43,11 +43,36 @@ function getEndDate() return Carbon::parse(auth()->user()->currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s'); } -function isSubscribed() +function is_subscription_active() { - return - auth()->user()?->currentTeam()?->subscription?->lemon_status === 'active' || - (auth()->user()?->currentTeam()?->subscription?->lemon_ends_at && - Carbon::parse(auth()->user()->currentTeam()->subscription->lemon_ends_at) > Carbon::now() - ) || auth()->user()->isInstanceAdmin(); + $team = auth()->user()?->currentTeam(); + if (!$team) { + return false; + } + $subscription = $team?->subscription; + if (!$subscription) { + return false; + } + + $is_active = $subscription->lemon_status === 'active'; + $is_instance_admin = auth()->user()->isInstanceAdmin(); + ray($is_instance_admin); + + return $is_active || $is_instance_admin; +} +function is_subscription_in_grace_period() +{ + $team = auth()->user()?->currentTeam(); + if (!$team) { + return false; + } + $subscription = $team?->subscription; + if (!$subscription) { + return false; + } + $is_instance_admin = auth()->user()->isInstanceAdmin(); + $is_still_grace_period = $subscription->lemon_ends_at && + Carbon::parse($subscription->lemon_ends_at) > Carbon::now(); + + return $is_still_grace_period || $is_instance_admin; } diff --git a/config/constants.php b/config/constants.php index a3c88c8e8..13097375e 100644 --- a/config/constants.php +++ b/config/constants.php @@ -6,4 +6,11 @@ 'expiration' => 10, ], ], + 'limits' => [ + 'server' => [ + 'basic' => 1, + 'pro' => 3, + 'ultimate' => 9999999999999999999, + ] + ] ]; diff --git a/config/coolify.php b/config/coolify.php index 51536284a..a933d12b4 100644 --- a/config/coolify.php +++ b/config/coolify.php @@ -3,9 +3,16 @@ return [ 'self_hosted' => env('SELF_HOSTED', true), 'license_url' => 'https://license.coolify.io', - 'lemon_squeezy_webhook_secret' => env('LEMON_SQUEEZY_WEBHOOK_SECRET'), - 'lemon_squeezy_checkout_id_monthly' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY'), - 'lemon_squeezy_checkout_id_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_YEARLY'), + 'lemon_squeezy_webhook_secret' => env('LEMON_SQUEEZY_WEBHOOK_SECRET', null), + 'lemon_squeezy_checkout_id_monthly_basic' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_BASIC', null), + 'lemon_squeezy_checkout_id_monthly_pro' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_PRO', null), + 'lemon_squeezy_checkout_id_monthly_ultimate' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_ULTIMATE', null), + 'lemon_squeezy_checkout_id_yearly_basic' => env('LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_BASIC', null), + 'lemon_squeezy_checkout_id_yearly_pro' => env('LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_PRO', null), + 'lemon_squeezy_checkout_id_yearly_ultimate' => env('LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_ULTIMATE', null), + 'lemon_squeezy_basic_plan_ids' => env('LEMON_SQUEEZY_BASIC_PLAN_IDS', ""), + 'lemon_squeezy_pro_plan_ids' => env('LEMON_SQUEEZY_PRO_PLAN_IDS', ""), + 'lemon_squeezy_ultimate_plan_ids' => env('LEMON_SQUEEZY_ULTIMATE_PLAN_IDS', ""), 'mux_enabled' => env('MUX_ENABLED', true), 'dev_webhook' => env('SERVEO_URL'), 'base_config_path' => env('BASE_CONFIG_PATH', '/_data/coolify'), diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index ae6b74e19..063d1e257 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -34,8 +34,15 @@ services: - PHP_PM_MAX_SPARE_SERVERS=10 - SELF_HOSTED - LEMON_SQUEEZY_WEBHOOK_SECRET - - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY - - LEMON_SQUEEZY_CHECKOUT_ID_YEARLY + - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_BASIC + - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_PRO + - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_ULTIMATE + - LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_BASIC + - LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_PRO + - LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_ULTIMATE + - LEMON_SQUEEZY_BASIC_PLAN_IDS + - LEMON_SQUEEZY_PRO_PLAN_IDS + - LEMON_SQUEEZY_ULTIMATE_PLAN_IDS ports: - "${APP_PORT:-8000}:80" expose: diff --git a/resources/views/components/layout-subscription.blade.php b/resources/views/components/layout-subscription.blade.php index d693b20cb..b2b8f67de 100644 --- a/resources/views/components/layout-subscription.blade.php +++ b/resources/views/components/layout-subscription.blade.php @@ -25,7 +25,7 @@
@livewireScriptsStart self-hosting in @@ -185,7 +185,7 @@ class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap
(save $29) Subscribe + href="{{ getSubscriptionLink('monthly_pro') }}">Subscribe SubscribeScale your business or self-hosting environment. @@ -255,7 +255,7 @@ class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap
(save $69) Subscribe + href="{{ getSubscriptionLink('monthly_ultimate') }}">Subscribe SubscribeDeploy complex infrastuctures and diff --git a/resources/views/livewire/server/new/by-ip.blade.php b/resources/views/livewire/server/new/by-ip.blade.php index 77ab1abb8..2c84e6cf3 100644 --- a/resources/views/livewire/server/new/by-ip.blade.php +++ b/resources/views/livewire/server/new/by-ip.blade.php @@ -1,29 +1,34 @@