From 7bfeb6c177d619af7b208da4bfbb2273fbfa2ce6 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 7 Jul 2023 21:35:29 +0200 Subject: [PATCH 01/28] feat: notify user of disk cleanup init --- app/Console/Kernel.php | 4 ++-- app/Jobs/DockerCleanupJob.php | 15 ++++++++++++--- app/Models/Server.php | 6 +++++- config/version.php | 2 +- versions.json | 2 +- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 1d20b7606..cbd91f662 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -14,12 +14,12 @@ protected function schedule(Schedule $schedule): void { if (isDev()) { $schedule->command('horizon:snapshot')->everyMinute(); - // $schedule->job(new DockerCleanupJob)->everyOddHour(); + $schedule->job(new DockerCleanupJob)->everyOddHour(); // $schedule->job(new InstanceAutoUpdateJob(true))->everyMinute(); } else { $schedule->command('horizon:snapshot')->everyFiveMinutes(); - $schedule->job(new DockerCleanupJob)->everyTenMinutes(); $schedule->job(new ProxyCheckJob)->everyFiveMinutes(); + $schedule->job(new DockerCleanupJob)->everyTenMinutes(); $schedule->job(new InstanceAutoUpdateJob)->everyTenMinutes(); } } diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index 117a9bd8a..96b2d668f 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -36,16 +36,25 @@ public function handle(): void } else { $docker_root_filesystem = instant_remote_process(['stat --printf=%m $(docker info --format "{{json .DockerRootDir}}" |sed \'s/"//g\')'], $server); } - $disk_usage = json_decode(instant_remote_process(['df -hP | awk \'BEGIN {printf"{\"disks\":["}{if($1=="Filesystem")next;if(a)printf",";printf"{\"mount\":\""$6"\",\"size\":\""$2"\",\"used\":\""$3"\",\"avail\":\""$4"\",\"use%\":\""$5"\"}";a++;}END{print"]}";}\''], $server), true); - $mount_point = collect(data_get($disk_usage, 'disks'))->where('mount', $docker_root_filesystem)->first(); - if (Str::of(data_get($mount_point, 'use%'))->trim()->replace('%', '')->value() >= $server->settings->cleanup_after_percentage) { + $disk_percentage_before = $this->get_disk_usage($server, $docker_root_filesystem); + if ($disk_percentage_before >= $server->settings->cleanup_after_percentage) { instant_remote_process(['docker image prune -af'], $server); instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server); instant_remote_process(['docker builder prune -af'], $server); + $disk_percentage_after = $this->get_disk_usage($server, $docker_root_filesystem); + if ($disk_percentage_after < $disk_percentage_before) { + ray('Saved ' . ($disk_percentage_before - $disk_percentage_after) . '% disk space on ' . $server->name); + } } } } catch (\Exception $e) { Log::error($e->getMessage()); } } + + private function get_disk_usage(Server $server, string $docker_root_filesystem) { + $disk_usage = json_decode(instant_remote_process(['df -hP | awk \'BEGIN {printf"{\"disks\":["}{if($1=="Filesystem")next;if(a)printf",";printf"{\"mount\":\""$6"\",\"size\":\""$2"\",\"used\":\""$3"\",\"avail\":\""$4"\",\"use%\":\""$5"\"}";a++;}END{print"]}";}\''], $server), true); + $mount_point = collect(data_get($disk_usage, 'disks'))->where('mount', $docker_root_filesystem)->first(); + return Str::of(data_get($mount_point, 'use%'))->trim()->replace('%', '')->value(); + } } \ No newline at end of file diff --git a/app/Models/Server.php b/app/Models/Server.php index d3fabe967..0d21e80b0 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -84,6 +84,10 @@ public function muxFilename() { return "{$this->ip}_{$this->port}_{$this->user}"; } + public function team() + { + return $this->belongsTo(Team::class); + } static public function ownedByCurrentTeam(array $select = ['*']) { $selectArray = collect($select)->concat(['id']); @@ -102,4 +106,4 @@ static public function destinationsByServer(string $server_id) $swarmDocker = collect($server->swarmDockers->all()); return $standaloneDocker->concat($swarmDocker); } -} +} \ No newline at end of file diff --git a/config/version.php b/config/version.php index 3a9c12a30..743126446 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Sun, 9 Jul 2023 15:32:19 +0200 Subject: [PATCH 02/28] fix: nginx try_files --- app/Jobs/ApplicationDeploymentJob.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 36a08fa03..7289f08a3 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -261,7 +261,7 @@ private function build_image() location / { root /usr/share/nginx/html; index index.html; - try_files \$uri \$uri/index.html \$uri/ /index.html =404; + try_files \$uri \$uri.html \$uri/index.html \$uri/ /index.html =404; } error_page 500 502 503 504 /50x.html; @@ -647,4 +647,4 @@ private function clone_repository() ); $this->commit = $this->saved_outputs->get('git_commit_sha'); } -} +} \ No newline at end of file From 6c955424cde640f2ebe422fff17f87a1c3d49d08 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 11 Jul 2023 11:11:51 +0200 Subject: [PATCH 03/28] fix: master is the default, not main --- .../Project/New/PublicGitRepository.php | 28 +++++++++++++------ bootstrap/helpers/github.php | 3 +- .../new/public-git-repository.blade.php | 2 +- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/Http/Livewire/Project/New/PublicGitRepository.php b/app/Http/Livewire/Project/New/PublicGitRepository.php index 9e24ed56d..1b4a3462d 100644 --- a/app/Http/Livewire/Project/New/PublicGitRepository.php +++ b/app/Http/Livewire/Project/New/PublicGitRepository.php @@ -8,7 +8,7 @@ use App\Models\Project; use App\Models\StandaloneDocker; use App\Models\SwarmDocker; -use Illuminate\Support\Facades\Log; +use Carbon\Carbon; use Livewire\Component; use Spatie\Url\Url; @@ -26,9 +26,9 @@ class PublicGitRepository extends Component public string $selected_branch = 'main'; public bool $is_static = false; public string|null $publish_directory = null; - public string $git_branch; + public string $git_branch = 'main'; public int $rate_limit_remaining = 0; - public int $rate_limit_reset = 0; + public $rate_limit_reset = 0; private GithubApp|GitlabApp $git_source; private string $git_host; @@ -67,6 +67,12 @@ public function instantSave() } $this->emit('success', 'Application settings updated!'); } + private function get_branch() + { + ['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = git_api(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}"); + $this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s.u'); + $this->branch_found = true; + } public function load_branch() { $this->branch_found = false; @@ -74,12 +80,18 @@ public function load_branch() 'repository_url' => 'required|url' ]); $this->get_git_source(); - try { - ['data' => $data, 'rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = git_api(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}"); - $this->branch_found = true; - } catch (\Throwable $e) { - return general_error_handler(err: $e, that: $this); + $this->get_branch(); + } catch (\Exception $e) { + } + + if (!$this->branch_found && $this->git_branch == 'main') { + try { + $this->git_branch = 'master'; + $this->get_branch(); + } catch (\Exception $e) { + return general_error_handler(err: $e, that: $this); + } } } private function get_git_source() diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index 4e4fdd7fc..8d18e6290 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -2,6 +2,7 @@ use App\Models\GithubApp; use App\Models\GitlabApp; +use Carbon\Carbon; use Illuminate\Support\Str; use Illuminate\Support\Facades\Http; use Lcobucci\JWT\Encoding\ChainedFormatter; @@ -64,7 +65,7 @@ function git_api(GithubApp|GitlabApp $source, string $endpoint, string $method = } $json = $response->json(); if ($response->failed() && $throwError) { - throw new \Exception("Failed to get data from {$source->name} with error:

" . $json['message']); + throw new \Exception("Failed to get data from {$source->name} with error:

" . $json['message'] . "

Rate Limit resets at: " . Carbon::parse((int)$response->header('X-RateLimit-Reset'))->format('Y-m-d H:i:s') . 'UTC'); } return [ 'rate_limit_remaining' => $response->header('X-RateLimit-Remaining'), diff --git a/resources/views/livewire/project/new/public-git-repository.blade.php b/resources/views/livewire/project/new/public-git-repository.blade.php index 63cfc0f54..21af86a2e 100644 --- a/resources/views/livewire/project/new/public-git-repository.blade.php +++ b/resources/views/livewire/project/new/public-git-repository.blade.php @@ -14,7 +14,7 @@ @if ($branch_found)
Rate limit remaining: {{ $rate_limit_remaining }}
-
Rate limit reset at: {{ date('Y-m-d H:i:s', substr($rate_limit_reset, 0, 10)) }}
+
Rate limit reset at: {{ $rate_limit_reset }}
From 4c88944286e48f53ac82234e0ff0dd3e66957d89 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 11 Jul 2023 11:16:29 +0200 Subject: [PATCH 04/28] fix: no ms in rate limit resets --- app/Http/Livewire/Project/New/PublicGitRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Livewire/Project/New/PublicGitRepository.php b/app/Http/Livewire/Project/New/PublicGitRepository.php index 1b4a3462d..8fbaf62fb 100644 --- a/app/Http/Livewire/Project/New/PublicGitRepository.php +++ b/app/Http/Livewire/Project/New/PublicGitRepository.php @@ -70,7 +70,7 @@ public function instantSave() private function get_branch() { ['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = git_api(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}"); - $this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s.u'); + $this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s'); $this->branch_found = true; } public function load_branch() From 3cc1731c1234a0aaf3a3dd0da8b8299eaa5e894f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 11 Jul 2023 11:51:06 +0200 Subject: [PATCH 05/28] refactor --- app/Http/Livewire/Server/Proxy.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/Http/Livewire/Server/Proxy.php b/app/Http/Livewire/Server/Proxy.php index 7049cd173..95e3e7e76 100644 --- a/app/Http/Livewire/Server/Proxy.php +++ b/app/Http/Livewire/Server/Proxy.php @@ -31,18 +31,6 @@ public function switchProxy() $this->server->proxy->type = null; $this->server->save(); } - public function installProxy() - { - if ( - $this->server->proxy->last_applied_settings && - $this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings - ) { - $this->saveConfiguration($this->server); - } - $activity = resolve(InstallProxy::class)($this->server); - $this->emit('newMonitorActivity', $activity->id); - } - public function setProxy(string $proxy_type) { $this->server->proxy->type = $proxy_type; @@ -56,6 +44,7 @@ public function stopProxy() ], $this->server); $this->server->proxy->status = 'exited'; $this->server->save(); + $this->server->refresh(); } public function saveConfiguration() { From a0b2868e95101bd7974e8c9e4d4ef75da691d96e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jul 2023 13:16:24 +0200 Subject: [PATCH 06/28] UI stuffs --- .../Livewire/Project/Application/Danger.php | 3 + .../Application/EnvironmentVariable/Add.php | 2 + .../Application/EnvironmentVariable/All.php | 12 +- .../Application/EnvironmentVariable/Show.php | 4 +- .../Project/Application/Storages/Show.php | 6 + app/Http/Livewire/Server/Form.php | 5 +- app/View/Components/Forms/Button.php | 30 +++ app/View/Components/Forms/Checkbox.php | 34 ++++ app/View/Components/Forms/Input.php | 4 +- app/View/Components/Forms/Select.php | 38 ++++ app/View/Components/Forms/Textarea.php | 43 ++++ app/View/Components/Modal.php | 33 ++++ package-lock.json | 15 +- package.json | 2 +- resources/css/app.css | 21 +- .../components/applications/actions.blade.php | 16 +- .../components/applications/links.blade.php | 13 +- .../views/components/forms/button.blade.php | 54 ++--- .../views/components/forms/checkbox.blade.php | 62 +++--- .../views/components/forms/input.blade.php | 88 ++++----- .../views/components/forms/select.blade.php | 49 ++--- .../views/components/forms/textarea.blade.php | 19 +- .../views/components/layout-simple.blade.php | 23 +-- resources/views/components/layout.blade.php | 187 +++++++++--------- resources/views/components/modal.blade.php | 53 +++++ .../views/components/naked-modal.blade.php | 2 +- .../livewire/private-key/change.blade.php | 2 +- .../project/application/danger.blade.php | 13 +- .../environment-variable/add.blade.php | 22 ++- .../environment-variable/all.blade.php | 36 ++-- .../environment-variable/show.blade.php | 20 +- .../project/application/previews.blade.php | 2 +- .../project/application/source.blade.php | 27 ++- .../application/storages/add.blade.php | 22 ++- .../application/storages/all.blade.php | 9 +- .../application/storages/show.blade.php | 12 +- .../views/livewire/run-command.blade.php | 2 +- .../views/livewire/server/form.blade.php | 14 +- .../views/livewire/server/proxy.blade.php | 4 +- .../livewire/server/proxy/deploy.blade.php | 15 +- .../livewire/source/github/change.blade.php | 4 +- .../views/livewire/team/delete.blade.php | 2 +- tailwind.config.js | 21 +- 43 files changed, 618 insertions(+), 427 deletions(-) create mode 100644 app/View/Components/Forms/Button.php create mode 100644 app/View/Components/Forms/Checkbox.php create mode 100644 app/View/Components/Forms/Select.php create mode 100644 app/View/Components/Forms/Textarea.php create mode 100644 app/View/Components/Modal.php create mode 100644 resources/views/components/modal.blade.php diff --git a/app/Http/Livewire/Project/Application/Danger.php b/app/Http/Livewire/Project/Application/Danger.php index 72873665e..4af90927e 100644 --- a/app/Http/Livewire/Project/Application/Danger.php +++ b/app/Http/Livewire/Project/Application/Danger.php @@ -4,14 +4,17 @@ use App\Models\Application; use Livewire\Component; +use Visus\Cuid2\Cuid2; class Danger extends Component { public Application $application; public array $parameters; + public string|null $modalId = null; public function mount() { + $this->modalId = new Cuid2(7); $this->parameters = getRouteParameters(); } public function delete() diff --git a/app/Http/Livewire/Project/Application/EnvironmentVariable/Add.php b/app/Http/Livewire/Project/Application/EnvironmentVariable/Add.php index 9b1b78a00..9b35de54f 100644 --- a/app/Http/Livewire/Project/Application/EnvironmentVariable/Add.php +++ b/app/Http/Livewire/Project/Application/EnvironmentVariable/Add.php @@ -29,6 +29,7 @@ public function mount() } public function submit() { + ray('submitting'); $this->validate(); $this->emitUp('submit', [ 'key' => $this->key, @@ -36,6 +37,7 @@ public function submit() 'is_build_time' => $this->is_build_time, 'is_preview' => $this->is_preview, ]); + $this->clear(); } public function clear() { diff --git a/app/Http/Livewire/Project/Application/EnvironmentVariable/All.php b/app/Http/Livewire/Project/Application/EnvironmentVariable/All.php index 1dcd605cd..f1ac75917 100644 --- a/app/Http/Livewire/Project/Application/EnvironmentVariable/All.php +++ b/app/Http/Livewire/Project/Application/EnvironmentVariable/All.php @@ -5,11 +5,17 @@ use App\Models\Application; use App\Models\EnvironmentVariable; use Livewire\Component; +use Visus\Cuid2\Cuid2; class All extends Component { public Application $application; + public string|null $modalId = null; protected $listeners = ['refreshEnvs', 'submit']; + public function mount() + { + $this->modalId = new Cuid2(7); + } public function refreshEnvs() { $this->application->refresh(); @@ -17,6 +23,11 @@ public function refreshEnvs() public function submit($data) { try { + $found = $this->application->environment_variables()->where('key', $data['key'])->first(); + if ($found) { + $this->emit('error', 'Environment variable already exists.'); + return; + } EnvironmentVariable::create([ 'key' => $data['key'], 'value' => $data['value'], @@ -27,7 +38,6 @@ public function submit($data) $this->application->refresh(); $this->emit('success', 'Environment variable added successfully.'); - $this->emit('clearAddEnv'); } catch (\Exception $e) { return general_error_handler(err: $e, that: $this); } diff --git a/app/Http/Livewire/Project/Application/EnvironmentVariable/Show.php b/app/Http/Livewire/Project/Application/EnvironmentVariable/Show.php index 61ecaf3de..c5260e77c 100644 --- a/app/Http/Livewire/Project/Application/EnvironmentVariable/Show.php +++ b/app/Http/Livewire/Project/Application/EnvironmentVariable/Show.php @@ -4,12 +4,13 @@ use App\Models\EnvironmentVariable as ModelsEnvironmentVariable; use Livewire\Component; +use Visus\Cuid2\Cuid2; class Show extends Component { public $parameters; public ModelsEnvironmentVariable $env; - + public string|null $modalId = null; protected $rules = [ 'env.key' => 'required|string', 'env.value' => 'required|string', @@ -22,6 +23,7 @@ class Show extends Component ]; public function mount() { + $this->modalId = new Cuid2(7); $this->parameters = getRouteParameters(); } public function submit() diff --git a/app/Http/Livewire/Project/Application/Storages/Show.php b/app/Http/Livewire/Project/Application/Storages/Show.php index 41c3c1fb8..ef6042baf 100644 --- a/app/Http/Livewire/Project/Application/Storages/Show.php +++ b/app/Http/Livewire/Project/Application/Storages/Show.php @@ -3,10 +3,12 @@ namespace App\Http\Livewire\Project\Application\Storages; use Livewire\Component; +use Visus\Cuid2\Cuid2; class Show extends Component { public $storage; + public string|null $modalId = null; protected $rules = [ 'storage.name' => 'required|string', 'storage.mount_path' => 'required|string', @@ -17,6 +19,10 @@ class Show extends Component 'mount_path' => 'mount', 'host_path' => 'host', ]; + public function mount() + { + $this->modalId = new Cuid2(7); + } public function submit() { $this->validate(); diff --git a/app/Http/Livewire/Server/Form.php b/app/Http/Livewire/Server/Form.php index fc98c05bf..c7c056665 100644 --- a/app/Http/Livewire/Server/Form.php +++ b/app/Http/Livewire/Server/Form.php @@ -5,6 +5,7 @@ use App\Actions\Server\InstallDocker; use App\Models\Server; use Livewire\Component; +use Visus\Cuid2\Cuid2; class Form extends Component { @@ -13,6 +14,7 @@ class Form extends Component public $dockerVersion; public string|null $wildcard_domain = null; public int $cleanup_after_percentage; + public string|null $modalId = null; protected $rules = [ 'server.name' => 'required|min:6', @@ -35,6 +37,7 @@ class Form extends Component ]; public function mount() { + $this->modalId = new Cuid2(7); $this->wildcard_domain = $this->server->settings->wildcard_domain; $this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage; } @@ -98,4 +101,4 @@ public function submit() $this->server->save(); $this->emit('success', 'Server updated successfully.'); } -} \ No newline at end of file +} diff --git a/app/View/Components/Forms/Button.php b/app/View/Components/Forms/Button.php new file mode 100644 index 000000000..8621db72a --- /dev/null +++ b/app/View/Components/Forms/Button.php @@ -0,0 +1,30 @@ +id)) $this->id = new Cuid2(7); + if (is_null($this->name)) $this->name = $this->id; + + $this->label = Str::title($this->label); + return view('components.forms.select'); + } +} diff --git a/app/View/Components/Forms/Textarea.php b/app/View/Components/Forms/Textarea.php new file mode 100644 index 000000000..f4d1f0bee --- /dev/null +++ b/app/View/Components/Forms/Textarea.php @@ -0,0 +1,43 @@ +id)) $this->id = new Cuid2(7); + if (is_null($this->name)) $this->name = $this->id; + + $this->label = Str::title($this->label); + return view('components.forms.textarea'); + } +} diff --git a/app/View/Components/Modal.php b/app/View/Components/Modal.php new file mode 100644 index 000000000..b317d3c44 --- /dev/null +++ b/app/View/Components/Modal.php @@ -0,0 +1,33 @@ +=16.9.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/daisyui" - }, - "peerDependencies": { - "postcss": "^8" } }, "node_modules/delayed-stream": { diff --git a/package.json b/package.json index 2884c7466..ed81d6a26 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "dependencies": { "@tailwindcss/typography": "0.5.9", "alpinejs": "3.12.2", - "daisyui": "3.0.3", + "daisyui": "3.2.1", "tailwindcss-scrollbar": "0.1.0" } } \ No newline at end of file diff --git a/resources/css/app.css b/resources/css/app.css index b788c6e6d..5bca7271f 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -2,19 +2,22 @@ @tailwind components; @tailwind utilities; -.scrollbar { - @apply scrollbar-thumb-coollabs-100 scrollbar-track-coolgray-200 scrollbar-w-2; -} html { @apply text-neutral-400; } body { @apply text-sm antialiased scrollbar; } +button[isError] { + @apply bg-red-600 hover:bg-red-500; +} +.scrollbar { + @apply scrollbar-thumb-coollabs-100 scrollbar-track-coolgray-200 scrollbar-w-2; +} .main { @apply max-w-screen-xl pt-4 pl-24 pr-10 mx-auto; } -input { +/* input { @apply w-full text-white rounded outline-none input input-sm h-7 placeholder:text-neutral-700 bg-coolgray-200 read-only:bg-coolgray-200/50 read-only:text-opacity-25; } input:not(input[type="checkbox"]) { @@ -22,14 +25,14 @@ input:not(input[type="checkbox"]) { } input[type="checkbox"] { @apply rounded toggle toggle-warning toggle-xs disabled:toggle-warning; -} +} */ -textarea { +/* textarea { @apply text-xs leading-5 text-white rounded textarea read-only:bg-coolgray-200/50 disabled:border-none read-only:text-opacity-25 placeholder:text-neutral-700 scrollbar bg-coolgray-200; } select { @apply font-normal text-white border-none rounded h-7 select select-xs disabled:bg-coolgray-200 disabled:opacity-50 placeholder:text-neutral-700 bg-coolgray-200; -} +} */ .label-text, label { @apply text-neutral-400; @@ -39,12 +42,12 @@ .loading { @apply w-4 text-warning; } -button[isWarning] { +/* button[isWarning] { @apply bg-red-600 hover:bg-red-500; } button[isHighlighted] { @apply text-white btn-primary; -} +} */ h1 { @apply text-3xl font-bold text-white; } diff --git a/resources/views/components/applications/actions.blade.php b/resources/views/components/applications/actions.blade.php index 141a9b151..fe5b235fc 100644 --- a/resources/views/components/applications/actions.blade.php +++ b/resources/views/components/applications/actions.blade.php @@ -8,7 +8,7 @@ class="relative text-xs text-white normal-case rounded -ml-44 min-w-max menu bg-coolgray-200"> @if ($application->status === 'running')
  • -
    @@ -19,10 +19,10 @@ class="relative text-xs text-white normal-case rounded -ml-44 min-w-max menu bg- Restart
  • -
    +
    @@ -35,7 +35,7 @@ class="relative text-xs text-white normal-case rounded -ml-44 min-w-max menu bg-
  • -
    @@ -49,7 +49,7 @@ class="relative text-xs text-white normal-case rounded -ml-44 min-w-max menu bg-
  • @else
  • -
    @@ -60,7 +60,7 @@ class="relative text-xs text-white normal-case rounded -ml-44 min-w-max menu bg-
  • -
    diff --git a/resources/views/components/applications/links.blade.php b/resources/views/components/applications/links.blade.php index 4a8e14672..b410cf0e1 100644 --- a/resources/views/components/applications/links.blade.php +++ b/resources/views/components/applications/links.blade.php @@ -6,7 +6,8 @@ @else -
    diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index f9d5dd043..f194e4529 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -18,4 +18,6 @@
    Applications, databases, etc...
  • + {{-- Subscribe --}} + diff --git a/routes/webhooks.php b/routes/webhooks.php index f91f10656..5bfdbfde4 100644 --- a/routes/webhooks.php +++ b/routes/webhooks.php @@ -4,6 +4,7 @@ use App\Models\ApplicationPreview; use App\Models\PrivateKey; use App\Models\GithubApp; +use App\Models\Webhook; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Route; use Illuminate\Support\Str; @@ -170,3 +171,42 @@ return general_error_handler(err: $e); } }); + +Route::post('/subscriptions/events', function () { + try { + $secret = config('coolify.lemon_squeezy_webhook_secret'); + $payload = request()->collect(); + $hash = hash_hmac('sha256', $payload, $secret); + $signature = ''; + + if (!hash_equals($hash, $signature)) { + return response('Invalid signature.', 400); + } + + $webhook = Webhook::create([ + 'type' => 'lemonsqueezy', + 'payload' => $payload + ]); + + $event = data_get($payload, 'meta.event_name'); + $email = data_get($payload, 'data.attributes.user_email'); + $update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method'); + switch ($event) { + case 'subscription_created': + + break; + } + + ray($payload); + $webhook->update([ + 'status' => 'success', + ]); + } catch (\Exception $e) { + $webhook->update([ + 'status' => 'failed', + 'failure_reason' => $e->getMessage() + ]); + } finally { + return response('OK'); + } +}); From e714e87ad67f46edfa08f394e1f73e210e2f6056 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jul 2023 15:45:42 +0200 Subject: [PATCH 10/28] fix --- bootstrap/helpers/shared.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 30b8f7d1b..5da7f9987 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -139,15 +139,13 @@ function getSubscriptionLink() $name = auth()->user()->name ?? null; $url = "https://store.coollabs.io/checkout/buy/d0b28c6a-9b57-40bf-8b84-89fbafde6526?"; if ($user_id) { - $url .= "checkout[custom][user_id]={$user_id}"; + $url .= "&checkout[custom][user_id]={$user_id}"; } if ($email) { - $url .= "?checkout[email]={$email}"; + $url .= "&checkout[email]={$email}"; } if ($name) { $url .= "&checkout[name]={$name}"; } - $url = "?checkout[custom][user_id]={$user_id}&checkout[email]={$email}&checkout[name]={$name}"; - ray($url); return $url; } From cac59e487308c247352cfe6dc4ed5783aa5aec90 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jul 2023 22:03:27 +0200 Subject: [PATCH 11/28] wip --- app/Http/Controllers/Controller.php | 12 +- app/Http/Kernel.php | 3 + app/Http/Middleware/LicenseValid.php | 7 +- app/Http/Middleware/SubscriptionValid.php | 36 +++ app/Models/User.php | 9 +- app/Models/Webhook.php | 5 +- app/View/Components/Forms/Button.php | 2 +- bootstrap/helpers/shared.php | 19 +- bootstrap/helpers/subscriptions.php | 34 +++ config/coolify.php | 2 +- ...7_13_115117_create_subscriptions_table.php | 8 +- .../views/components/forms/select.blade.php | 2 +- resources/views/components/layout.blade.php | 8 +- resources/views/components/navbar.blade.php | 219 +++++++++--------- .../views/components/team/navbar.blade.php | 27 ++- resources/views/dashboard.blade.php | 2 - .../views/livewire/team/delete.blade.php | 2 + .../views/livewire/team/member.blade.php | 2 +- resources/views/team/members.blade.php | 43 ++++ resources/views/team/show.blade.php | 73 +++--- routes/web.php | 1 + routes/webhooks.php | 123 +++++++--- 22 files changed, 405 insertions(+), 234 deletions(-) create mode 100644 app/Http/Middleware/SubscriptionValid.php create mode 100644 bootstrap/helpers/subscriptions.php create mode 100644 resources/views/team/members.blade.php diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 673409ba6..96cd1774d 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -62,13 +62,23 @@ public function emails() public function team() { $invitations = []; - if (auth()->user()->isAdmin()) { + if (auth()->user()->isAdminFromSession()) { $invitations = TeamInvitation::whereTeamId(auth()->user()->currentTeam()->id)->get(); } return view('team.show', [ 'invitations' => $invitations, ]); } + public function members() + { + $invitations = []; + if (auth()->user()->isAdminFromSession()) { + $invitations = TeamInvitation::whereTeamId(auth()->user()->currentTeam()->id)->get(); + } + return view('team.members', [ + 'invitations' => $invitations, + ]); + } public function acceptInvitation() { try { diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index ef84b9abb..2080e9cca 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -22,6 +22,7 @@ class Kernel extends HttpKernel \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, \App\Http\Middleware\LicenseValid::class, + ]; /** @@ -37,6 +38,8 @@ class Kernel extends HttpKernel \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, + \App\Http\Middleware\SubscriptionValid::class, + ], 'api' => [ diff --git a/app/Http/Middleware/LicenseValid.php b/app/Http/Middleware/LicenseValid.php index aeee9a5ac..bc5fb53e5 100644 --- a/app/Http/Middleware/LicenseValid.php +++ b/app/Http/Middleware/LicenseValid.php @@ -16,10 +16,7 @@ class LicenseValid */ public function handle(Request $request, Closure $next): Response { - if (isCloud()) { - if (isDev()) { - return $next($request); - } + if (isCloud() && !isDev()) { $value = Cache::get('license_key'); if (!$value) { ray($request->path()); @@ -30,4 +27,4 @@ public function handle(Request $request, Closure $next): Response } return $next($request); } -} +} \ No newline at end of file diff --git a/app/Http/Middleware/SubscriptionValid.php b/app/Http/Middleware/SubscriptionValid.php new file mode 100644 index 000000000..c1ce35dbd --- /dev/null +++ b/app/Http/Middleware/SubscriptionValid.php @@ -0,0 +1,36 @@ +user()?->currentTeam()?->subscription && $request->user()?->currentTeam()->subscription?->lemon_status !== 'active') { + if (!in_array($request->path(), $allowed_paths)) { + return redirect('team'); + } + } + } + return $next($request); + } +} \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php index da42a977f..aa08ad22b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -61,8 +61,10 @@ public function routeNotificationForEmail() { return $this->email; } - - public function isAdmin() + public function isAdmin() { + return $this->pivot->role === 'admin' || $this->pivot->role === 'owner'; + } + public function isAdminFromSession() { if (auth()->user()->id === 0) { return true; @@ -89,6 +91,9 @@ public function isInstanceAdmin() }); return $found_root_team->count() > 0; } + public function personalTeam() { + return $this->teams()->where('personal_team', true)->first(); + } public function teams() { return $this->belongsToMany(Team::class)->withPivot('role'); diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 09afb9898..079b926be 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -8,4 +8,7 @@ class Webhook extends Model { protected $guarded = []; -} + protected $casts = [ + 'payload' => 'encrypted', + ]; +} \ No newline at end of file diff --git a/app/View/Components/Forms/Button.php b/app/View/Components/Forms/Button.php index 8621db72a..0f33192f7 100644 --- a/app/View/Components/Forms/Button.php +++ b/app/View/Components/Forms/Button.php @@ -27,4 +27,4 @@ public function render(): View|Closure|string { return view('components.forms.button'); } -} +} \ No newline at end of file diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 5da7f9987..3fd5b1833 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -131,21 +131,4 @@ function isDev() function isCloud() { return !config('coolify.self_hosted'); -} -function getSubscriptionLink() -{ - $user_id = auth()->user()->id; - $email = auth()->user()->email ?? null; - $name = auth()->user()->name ?? null; - $url = "https://store.coollabs.io/checkout/buy/d0b28c6a-9b57-40bf-8b84-89fbafde6526?"; - if ($user_id) { - $url .= "&checkout[custom][user_id]={$user_id}"; - } - if ($email) { - $url .= "&checkout[email]={$email}"; - } - if ($name) { - $url .= "&checkout[name]={$name}"; - } - return $url; -} +} \ No newline at end of file diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php new file mode 100644 index 000000000..af4fc2131 --- /dev/null +++ b/bootstrap/helpers/subscriptions.php @@ -0,0 +1,34 @@ +user()->id; + $team_id = auth()->user()->currentTeam()->id ?? null; + $email = auth()->user()->email ?? null; + $name = auth()->user()->name ?? null; + $url = "https://store.coollabs.io/checkout/buy/d0b28c6a-9b57-40bf-8b84-89fbafde6526?"; + if ($user_id) { + $url .= "&checkout[custom][user_id]={$user_id}"; + } + if (isset($team_id)) { + $url .= "&checkout[custom][team_id]={$team_id}"; + } + if ($email) { + $url .= "&checkout[email]={$email}"; + } + if ($name) { + $url .= "&checkout[name]={$name}"; + } + return $url; +} +function getPaymentLink() { + return auth()->user()->currentTeam()->subscription->lemon_update_payment_menthod_url; +} +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 diff --git a/config/coolify.php b/config/coolify.php index 044bb4563..715c3a3ea 100644 --- a/config/coolify.php +++ b/config/coolify.php @@ -8,4 +8,4 @@ '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_115117_create_subscriptions_table.php b/database/migrations/2023_07_13_115117_create_subscriptions_table.php index e31fc1c3f..08aeaaa17 100644 --- a/database/migrations/2023_07_13_115117_create_subscriptions_table.php +++ b/database/migrations/2023_07_13_115117_create_subscriptions_table.php @@ -13,14 +13,16 @@ public function up(): void { Schema::create('subscriptions', function (Blueprint $table) { $table->id(); + $table->string('lemon_subscription_id'); $table->string('lemon_order_id'); $table->string('lemon_product_id'); $table->string('lemon_variant_id'); + $table->string('lemon_variant_name'); $table->string('lemon_customer_id'); $table->string('lemon_status'); - $table->string('lemon_trial_ends_at'); + $table->string('lemon_trial_ends_at')->nullable(); $table->string('lemon_renews_at'); - $table->string('lemon_ends_at'); + $table->string('lemon_ends_at')->nullable(); $table->string('lemon_update_payment_menthod_url'); $table->foreignId('team_id'); $table->timestamps(); @@ -34,4 +36,4 @@ public function down(): void { Schema::dropIfExists('subscriptions'); } -}; +}; \ No newline at end of file diff --git a/resources/views/components/forms/select.blade.php b/resources/views/components/forms/select.blade.php index fc4e34e3c..bf9eeefba 100644 --- a/resources/views/components/forms/select.blade.php +++ b/resources/views/components/forms/select.blade.php @@ -12,7 +12,7 @@ class="flex items-center gap-1 mb-2 text-sm font-medium text-neutral-400">{{ $la @endif diff --git a/resources/views/components/layout.blade.php b/resources/views/components/layout.blade.php index 5610dfff2..efd60e5e9 100644 --- a/resources/views/components/layout.blade.php +++ b/resources/views/components/layout.blade.php @@ -30,9 +30,11 @@ @auth -
    - -
    + @if (isSubscribed()) +
    + +
    + @endif
    {{ $slot }}
    diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index 3848599c0..9ffa2658e 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -1,110 +1,115 @@ @auth - +@endauth diff --git a/resources/views/components/team/navbar.blade.php b/resources/views/components/team/navbar.blade.php index 21a8c2f82..482858161 100644 --- a/resources/views/components/team/navbar.blade.php +++ b/resources/views/components/team/navbar.blade.php @@ -20,15 +20,22 @@ @endif -