diff --git a/app/Actions/License/CheckResaleLicense.php b/app/Actions/License/CheckResaleLicense.php new file mode 100644 index 000000000..9aa6e8486 --- /dev/null +++ b/app/Actions/License/CheckResaleLicense.php @@ -0,0 +1,64 @@ +resale_license) { + return; + } + ray('Checking license key'); + $data = Http::withHeaders([ + 'Accept' => 'application/json', + ])->post('https://api.lemonsqueezy.com/v1/licenses/validate', [ + 'license_key' => $settings->resale_license, + 'instance_name' => $instance_id, + ])->throw()->json(); + $product_id = data_get($data, 'meta.product_id'); + + if (isDev()) { + $valid_product_id = 93221; + } else { + $valid_product_id = 93222; + } + if ($product_id !== $valid_product_id) { + throw new \Exception('Invalid product id'); + } + ray('Valid Product Id'); + + ['valid' => $valid, 'license_key' => $license_key] = $data; + + if ($valid) { + if (data_get($license_key, 'status') === 'inactive') { + Http::withHeaders([ + 'Accept' => 'application/json', + ])->post('https://api.lemonsqueezy.com/v1/licenses/activate', [ + 'license_key' => $settings->resale_license, + 'instance_name' => $instance_id, + ])->throw()->json(); + } + $settings->update([ + 'is_resale_license_active' => true, + ]); + return; + } + throw new \Exception('Invalid license key'); + } catch (\Throwable $th) { + ray($th); + $settings->update([ + 'resale_license' => null, + 'is_resale_license_active' => false, + ]); + throw $th; + } + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index cbd91f662..c35dc152f 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -2,9 +2,11 @@ namespace App\Console; +use App\Jobs\CheckResaleLicenseJob; use App\Jobs\InstanceAutoUpdateJob; use App\Jobs\ProxyCheckJob; use App\Jobs\DockerCleanupJob; +use App\Jobs\CheckResaleLicenseKeys; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -14,6 +16,7 @@ protected function schedule(Schedule $schedule): void { if (isDev()) { $schedule->command('horizon:snapshot')->everyMinute(); + $schedule->job(new CheckResaleLicenseJob)->everyMinute(); $schedule->job(new DockerCleanupJob)->everyOddHour(); // $schedule->job(new InstanceAutoUpdateJob(true))->everyMinute(); } else { @@ -29,4 +32,4 @@ protected function commands(): void require base_path('routes/console.php'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 96cd1774d..98faaad2e 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -17,9 +17,11 @@ class Controller extends BaseController { use AuthorizesRequests, ValidatesRequests; - public function license() + public function subscription() { - return view('license'); + return view('subscription', [ + 'settings' => InstanceSettings::get() + ]); } public function dashboard() { diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 2080e9cca..06777812f 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -21,7 +21,6 @@ class Kernel extends HttpKernel \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, - \App\Http\Middleware\LicenseValid::class, ]; diff --git a/app/Http/Livewire/CheckLicense.php b/app/Http/Livewire/CheckLicense.php new file mode 100644 index 000000000..59942c6a6 --- /dev/null +++ b/app/Http/Livewire/CheckLicense.php @@ -0,0 +1,42 @@ + 'nullable', + 'settings.is_resale_license_active' => 'nullable', + ]; + protected $validationAttributes = [ + 'settings.resale_license' => 'License', + 'instance_id' => 'Instance Id (Do not change this)', + 'settings.is_resale_license_active' => 'Is License Active', + ]; + public function mount() + { + $this->instance_id = config('app.id'); + $this->settings = InstanceSettings::get(); + } + public function submit() + { + $this->validate(); + $this->settings->save(); + if ($this->settings->resale_license) { + try { + resolve(CheckResaleLicense::class)(); + $this->emit('reloadWindow'); + } catch (\Throwable $th) { + session()->flash('error', 'License is not valid. Please contact support.'); + ray($th->getMessage()); + return redirect()->to('/subscription'); + } + } + } +} diff --git a/app/Http/Livewire/Settings/Configuration.php b/app/Http/Livewire/Settings/Configuration.php index 41eae45fd..889e72009 100644 --- a/app/Http/Livewire/Settings/Configuration.php +++ b/app/Http/Livewire/Settings/Configuration.php @@ -5,6 +5,7 @@ use App\Jobs\ProxyCheckJob; use App\Models\InstanceSettings as ModelsInstanceSettings; use App\Models\Server; +use Illuminate\Support\Facades\Http; use Livewire\Component; use Spatie\Url\Url; use Symfony\Component\Yaml\Yaml; @@ -21,11 +22,13 @@ class Configuration extends Component protected $rules = [ 'settings.fqdn' => 'nullable', + 'settings.resale_license' => 'nullable', 'settings.public_port_min' => 'required', 'settings.public_port_max' => 'required', ]; protected $validationAttributes = [ 'settings.fqdn' => 'FQDN', + 'settings.resale_license' => 'Resale License', 'settings.public_port_min' => 'Public port min', 'settings.public_port_max' => 'Public port max', ]; diff --git a/app/Http/Middleware/LicenseValid.php b/app/Http/Middleware/LicenseValid.php index bc5fb53e5..52d5840e7 100644 --- a/app/Http/Middleware/LicenseValid.php +++ b/app/Http/Middleware/LicenseValid.php @@ -16,15 +16,21 @@ class LicenseValid */ public function handle(Request $request, Closure $next): Response { - if (isCloud() && !isDev()) { + if (isCloud()) { $value = Cache::get('license_key'); + if (isDev()) { + $value = true; + } if (!$value) { - ray($request->path()); if ($request->path() !== 'license' && $request->path() !== 'livewire/message/license') { return redirect('license'); } + } else { + if ($request->path() === 'license' || $request->path() === 'livewire/message/license') { + return redirect('home'); + } } } return $next($request); } -} \ No newline at end of file +} diff --git a/app/Http/Middleware/SubscriptionValid.php b/app/Http/Middleware/SubscriptionValid.php index c1ce35dbd..ab2d9eb1f 100644 --- a/app/Http/Middleware/SubscriptionValid.php +++ b/app/Http/Middleware/SubscriptionValid.php @@ -4,33 +4,34 @@ use Closure; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Cache; use Symfony\Component\HttpFoundation\Response; class SubscriptionValid { - /** - * Handle an incoming request. - * - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next - */ + public function handle(Request $request, Closure $next): Response { $allowed_paths = [ - 'team', - 'livewire/message/team', + 'subscription', 'login', 'register', - 'livewire/message/switch-team', 'logout', + 'livewire/message/check-license', + 'livewire/message/switch-team', ]; - if (isCloud()) { - if (!$request->user()?->currentTeam()?->subscription && $request->user()?->currentTeam()->subscription?->lemon_status !== 'active') { + if (isCloud() && !isSubscribed()) { + ray('SubscriptionValid Middleware'); if (!in_array($request->path(), $allowed_paths)) { - return redirect('team'); + return redirect('subscription'); + } else { + return $next($request); } + } else { + if ($request->path() === 'subscription') { + return redirect('/'); + } else { + return $next($request); } } - return $next($request); } -} \ No newline at end of file +} diff --git a/app/Jobs/CheckResaleLicenseJob.php b/app/Jobs/CheckResaleLicenseJob.php new file mode 100644 index 000000000..0e925c569 --- /dev/null +++ b/app/Jobs/CheckResaleLicenseJob.php @@ -0,0 +1,32 @@ +email; } - public function isAdmin() { + public function isAdmin() + { return $this->pivot->role === 'admin' || $this->pivot->role === 'owner'; } public function isAdminFromSession() @@ -91,7 +92,8 @@ public function isInstanceAdmin() }); return $found_root_team->count() > 0; } - public function personalTeam() { + public function personalTeam() + { return $this->teams()->where('personal_team', true)->first(); } public function teams() diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 3fd5b1833..f45825686 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -130,5 +130,5 @@ function isDev() } function isCloud() { - return !config('coolify.self_hosted'); -} \ No newline at end of file + return (bool)config('coolify.self_hosted') === false; +} diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php index af4fc2131..319212fd4 100644 --- a/bootstrap/helpers/subscriptions.php +++ b/bootstrap/helpers/subscriptions.php @@ -23,12 +23,21 @@ function getSubscriptionLink() } return $url; } -function getPaymentLink() { +function getPaymentLink() +{ return auth()->user()->currentTeam()->subscription->lemon_update_payment_menthod_url; } -function getRenewDate() { +function getRenewDate() +{ return Carbon::parse(auth()->user()->currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s'); } -function isSubscribed() { - return isCloud() && auth()->user()->currentTeam()->subscription?->lemon_status === 'active'; -} \ No newline at end of file +function getEndDate() +{ + return Carbon::parse(auth()->user()->currentTeam()->subscription->lemon_ends_at)->format('Y-M-d H:i:s'); +} +function isSubscribed() +{ + 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()); +} diff --git a/config/app.php b/config/app.php index a3347a8cd..78df1ec8a 100644 --- a/config/app.php +++ b/config/app.php @@ -4,6 +4,7 @@ return [ + 'id' => env('APP_ID'), 'port' => env('APP_PORT', 8000), /* |-------------------------------------------------------------------------- diff --git a/config/coolify.php b/config/coolify.php index 715c3a3ea..08634313d 100644 --- a/config/coolify.php +++ b/config/coolify.php @@ -3,9 +3,8 @@ return [ 'self_hosted' => env('SELF_HOSTED', true), 'lemon_squeezy_webhook_secret' => env('LEMON_SQUEEZY_WEBHOOK_SECRET'), - 'lemon_squeezy_product_id' => env('LEMON_SQUEEZY_PRODUCT_ID'), 'mux_enabled' => env('MUX_ENABLED', true), 'dev_webhook' => env('SERVEO_URL'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), 'proxy_config_path' => env('BASE_CONFIG_PATH', '/data/coolify') . "/proxy", -]; \ No newline at end of file +]; diff --git a/database/migrations/2023_07_13_120721_add_license_to_instance_settings.php b/database/migrations/2023_07_13_120721_add_license_to_instance_settings.php new file mode 100644 index 000000000..69598ece1 --- /dev/null +++ b/database/migrations/2023_07_13_120721_add_license_to_instance_settings.php @@ -0,0 +1,30 @@ +boolean('is_resale_license_active')->default(false); + $table->string('resale_license')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('instance_settings', function (Blueprint $table) { + $table->dropColumn('is_resale_license_active'); + $table->dropColumn('resale_license'); + }); + } +}; diff --git a/database/seeders/TeamSeeder.php b/database/seeders/TeamSeeder.php index d1b41a42e..67c5ec489 100644 --- a/database/seeders/TeamSeeder.php +++ b/database/seeders/TeamSeeder.php @@ -16,7 +16,6 @@ public function run(): void $root_user_personal_team->save(); $normal_user_in_root_team->teams()->attach($root_user_personal_team); - $normal_user_not_in_root_team = User::find(2); $normal_user_in_root_team_personal_team = Team::find(1); $normal_user_not_in_root_team->teams()->attach($normal_user_in_root_team_personal_team, ['role' => 'admin']); diff --git a/resources/views/components/layout-subscription.blade.php b/resources/views/components/layout-subscription.blade.php new file mode 100644 index 000000000..c87f0b104 --- /dev/null +++ b/resources/views/components/layout-subscription.blade.php @@ -0,0 +1,40 @@ + + + + + + + + + @env('local') + Coolify - localhost + @endenv + @env('production') + {{ $title ?? 'Coolify' }} + @endenv + + @vite(['resources/js/app.js', 'resources/css/app.css']) + + @livewireStyles + + + + @livewireScripts + + +
+ {{ $slot }} +
+ + + + + diff --git a/resources/views/components/layout.blade.php b/resources/views/components/layout.blade.php index efd60e5e9..5610dfff2 100644 --- a/resources/views/components/layout.blade.php +++ b/resources/views/components/layout.blade.php @@ -30,11 +30,9 @@ @auth - @if (isSubscribed()) -
- -
- @endif +
+ +
{{ $slot }}
diff --git a/resources/views/components/navbar-subscription.blade.php b/resources/views/components/navbar-subscription.blade.php new file mode 100644 index 000000000..de98e1520 --- /dev/null +++ b/resources/views/components/navbar-subscription.blade.php @@ -0,0 +1,20 @@ +@auth + +@endauth diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index 9ffa2658e..bf0a2f3a2 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -1,5 +1,5 @@ @auth -