feat: cloud
This commit is contained in:
parent
b941f35812
commit
e4279bf257
24
app/Http/Controllers/ServerController.php
Normal file
24
app/Http/Controllers/ServerController.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\PrivateKey;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
use AuthorizesRequests, ValidatesRequests;
|
||||
|
||||
public function new_server()
|
||||
{
|
||||
$servers = auth()->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(),
|
||||
]);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -6,4 +6,11 @@
|
||||
'expiration' => 10,
|
||||
],
|
||||
],
|
||||
'limits' => [
|
||||
'server' => [
|
||||
'basic' => 1,
|
||||
'pro' => 3,
|
||||
'ultimate' => 9999999999999999999,
|
||||
]
|
||||
]
|
||||
];
|
||||
|
@ -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'),
|
||||
|
@ -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:
|
||||
|
@ -25,7 +25,7 @@
|
||||
<body>
|
||||
@livewireScripts
|
||||
<x-toaster-hub />
|
||||
@if (auth()->user()->isInstanceAdmin())
|
||||
@if (auth()->user()->isInstanceAdmin() || is_subscription_in_grace_period())
|
||||
<div class="fixed top-3 left-4" id="vue">
|
||||
<magic-bar></magic-bar>
|
||||
</div>
|
||||
|
6
resources/views/components/limit-reached.blade.php
Normal file
6
resources/views/components/limit-reached.blade.php
Normal file
@ -0,0 +1,6 @@
|
||||
<div class="flex flex-col items-center justify-center h-screen">
|
||||
<span class="text-xl font-bold text-white">You have reached the limit of {{ $name }} you can create.</span>
|
||||
<span>Please <a class="text-white underline "href="{{ route('team.show') }}">upgrade your
|
||||
subscription<a /> to create more
|
||||
{{ $name }}.</span>
|
||||
</div>
|
@ -111,7 +111,7 @@ class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap
|
||||
<span x-show="selected === 'yearly'" x-cloak class="text-warning">(save $6)</span>
|
||||
|
||||
<a x-show="selected === 'monthly'" x-cloak aria-describedby="tier-basic" class="buyme"
|
||||
href="{{ getSubscriptionLink('monthly') }}">Subscribe</a>
|
||||
href="{{ getSubscriptionLink('monthly_basic') }}">Subscribe</a>
|
||||
<a x-show="selected === 'yearly'" x-cloak aria-describedby="tier-basic" class="buyme"
|
||||
href="{{ getSubscriptionLink('yearly') }}">Subscribe</a>
|
||||
<p class="mt-10 text-sm leading-6 text-white h-[6.5rem]">Start self-hosting in
|
||||
@ -185,7 +185,7 @@ class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap
|
||||
</p>
|
||||
<span x-show="selected === 'yearly'" x-cloak class="text-warning">(save $29)</span>
|
||||
<a x-show="selected === 'monthly'" x-cloak aria-describedby="tier-essential" class="buyme"
|
||||
href="{{ getSubscriptionLink('monthly') }}">Subscribe</a>
|
||||
href="{{ getSubscriptionLink('monthly_pro') }}">Subscribe</a>
|
||||
<a x-show="selected === 'yearly'" x-cloak aria-describedby="tier-essential" class="buyme"
|
||||
href="{{ getSubscriptionLink('yearly') }}">Subscribe</a>
|
||||
<p class="h-20 mt-10 text-sm leading-6 text-white">Scale 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
|
||||
</p>
|
||||
<span x-show="selected === 'yearly'" x-cloak class="text-warning">(save $69)</span>
|
||||
<a x-show="selected === 'monthly'" x-cloak aria-describedby="tier-growth" class="buyme"
|
||||
href="{{ getSubscriptionLink('monthly') }}">Subscribe</a>
|
||||
href="{{ getSubscriptionLink('monthly_ultimate') }}">Subscribe</a>
|
||||
<a x-show="selected === 'yearly'" x-cloak aria-describedby="tier-growth" class="buyme"
|
||||
href="{{ getSubscriptionLink('yearly') }}">Subscribe</a>
|
||||
<p class="h-20 mt-10 text-sm leading-6 text-white">Deploy complex infrastuctures and
|
||||
|
@ -1,29 +1,34 @@
|
||||
<div>
|
||||
<h1>Create a new Server</h1>
|
||||
<div class="subtitle ">Servers are the main blocks of your infrastructure.</div>
|
||||
<form class="flex flex-col gap-2" wire:submit.prevent='submit'>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="name" label="Name" required />
|
||||
<x-forms.input id="description" label="Description" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="ip" label="IP Address" required
|
||||
helper="Could be IP Address (127.0.0.1) or Domain Name (duckduckgo.com)." />
|
||||
<x-forms.input id="user" label="User" required />
|
||||
<x-forms.input type="number" id="port" label="Port" required />
|
||||
</div>
|
||||
<x-forms.select label="Private Key" id="private_key_id">
|
||||
<option disabled>Select a private key</option>
|
||||
@foreach ($private_keys as $key)
|
||||
@if ($loop->first)
|
||||
<option selected value="{{ $key->id }}">{{ $key->name }}</option>
|
||||
@else
|
||||
<option value="{{ $key->id }}">{{ $key->name }}</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</x-forms.select>
|
||||
<x-forms.button type="submit">
|
||||
Save New Server
|
||||
</x-forms.button>
|
||||
</form>
|
||||
@if ($limit_reached)
|
||||
<x-limit-reached name="servers" />
|
||||
@else
|
||||
<h1>Create a new Server</h1>
|
||||
<div class="subtitle ">Servers are the main blocks of your infrastructure.</div>
|
||||
<form class="flex flex-col gap-2" wire:submit.prevent='submit'>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="name" label="Name" required />
|
||||
<x-forms.input id="description" label="Description" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="ip" label="IP Address" required
|
||||
helper="Could be IP Address (127.0.0.1) or Domain Name (duckduckgo.com)." />
|
||||
<x-forms.input id="user" label="User" required />
|
||||
<x-forms.input type="number" id="port" label="Port" required />
|
||||
</div>
|
||||
<x-forms.select label="Private Key" id="private_key_id">
|
||||
<option disabled>Select a private key</option>
|
||||
@foreach ($private_keys as $key)
|
||||
@if ($loop->first)
|
||||
<option selected value="{{ $key->id }}">{{ $key->name }}</option>
|
||||
@else
|
||||
<option value="{{ $key->id }}">{{ $key->name }}</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</x-forms.select>
|
||||
<x-forms.button type="submit">
|
||||
Save New Server
|
||||
</x-forms.button>
|
||||
</form>
|
||||
|
||||
@endif
|
||||
</div>
|
||||
|
@ -35,6 +35,11 @@
|
||||
<x-use-magic-bar link="/server/new" />
|
||||
</div>
|
||||
@endforelse
|
||||
@isset($error)
|
||||
<div class="text-center text-error">
|
||||
<span>{{ $error }}</span>
|
||||
</div>
|
||||
@endisset
|
||||
<script>
|
||||
function goto(uuid) {
|
||||
window.location.href = '/server/' + uuid;
|
||||
|
@ -4,6 +4,6 @@
|
||||
<div class="subtitle">You need to create a private key before you can create a server.</div>
|
||||
<livewire:private-key.create from="server" />
|
||||
@else
|
||||
<livewire:server.new.by-ip :private_keys="$private_keys" />
|
||||
<livewire:server.new.by-ip :private_keys="$private_keys" :limit_reached="$limit_reached" />
|
||||
@endif
|
||||
</x-layout>
|
||||
|
@ -5,19 +5,25 @@
|
||||
<livewire:team.form />
|
||||
@if (is_cloud())
|
||||
<div class="pb-8">
|
||||
<h3>Subscription</h3>
|
||||
<h2>Subscription</h2>
|
||||
@if (data_get(auth()->user()->currentTeam(),
|
||||
'subscription'))
|
||||
<div>Status: {{ auth()->user()->currentTeam()->subscription->lemon_status }}</div>
|
||||
<div>Type: {{ auth()->user()->currentTeam()->subscription->lemon_variant_name }}</div>
|
||||
@if (auth()->user()->currentTeam()->subscription->lemon_status === 'cancelled')
|
||||
<div class="pb-4">Subscriptions ends at: {{ getRenewDate() }}</div>
|
||||
<x-forms.button><a class="text-white" href="{{ route('subscription') }}">Subscribe
|
||||
Again</a>
|
||||
<x-forms.button class="bg-coollabs-gradient"><a class="text-white hover:no-underline"
|
||||
href="{{ route('subscription') }}">Resume Subscription</a>
|
||||
</x-forms.button>
|
||||
<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
|
||||
contact
|
||||
us.</a></div>
|
||||
@else
|
||||
<div class="pb-4">Renews at: {{ getRenewDate() }}</div>
|
||||
@endif
|
||||
|
||||
|
||||
<x-forms.button><a class="text-white hover:no-underline" href="{{ getPaymentLink() }}">Update Payment
|
||||
Details</a>
|
||||
</x-forms.button>
|
||||
|
@ -4,9 +4,8 @@
|
||||
->currentTeam()" />
|
||||
<div class="flex items-start gap-2">
|
||||
<h2 class="pb-4">S3 Storages</h2>
|
||||
<x-forms.button class="btn">
|
||||
<a class="text-white hover:no-underline" href="/team/storages/new">+ Add</a>
|
||||
</x-forms.button>
|
||||
<a class="text-white hover:no-underline" href="/team/storages/new"> <x-forms.button class="btn">+ Add
|
||||
</x-forms.button></a>
|
||||
</div>
|
||||
<div class="grid gap-2 lg:grid-cols-2">
|
||||
@forelse ($s3 as $storage)
|
||||
|
@ -5,6 +5,7 @@
|
||||
use App\Http\Controllers\DatabaseController;
|
||||
use App\Http\Controllers\MagicController;
|
||||
use App\Http\Controllers\ProjectController;
|
||||
use App\Http\Controllers\ServerController;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\GitlabApp;
|
||||
use App\Models\InstanceSettings;
|
||||
@ -71,9 +72,7 @@
|
||||
Route::get('/servers', fn () => view('server.all', [
|
||||
'servers' => Server::ownedByCurrentTeam()->get()
|
||||
]))->name('server.all');
|
||||
Route::get('/server/new', fn () => view('server.create', [
|
||||
'private_keys' => PrivateKey::ownedByCurrentTeam()->get(),
|
||||
]))->name('server.create');
|
||||
Route::get('/server/new', [ServerController::class, 'new_server'])->name('server.create');
|
||||
Route::get('/server/{server_uuid}', fn () => view('server.show', [
|
||||
'server' => Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->firstOrFail(),
|
||||
]))->name('server.show');
|
||||
|
Loading…
Reference in New Issue
Block a user