From 1047e8f948eec07f67dccc5453c12c32e67f8457 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Mon, 10 Jun 2024 15:11:12 +0200 Subject: [PATCH 01/27] Make egg list searchable and sortable (#359) * make egg list searchable and sortable * run pint --- app/Filament/Resources/EggResource/Pages/ListEggs.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Filament/Resources/EggResource/Pages/ListEggs.php b/app/Filament/Resources/EggResource/Pages/ListEggs.php index 71adf94d7..c3119b17d 100644 --- a/app/Filament/Resources/EggResource/Pages/ListEggs.php +++ b/app/Filament/Resources/EggResource/Pages/ListEggs.php @@ -22,19 +22,20 @@ class ListEggs extends ListRecords public function table(Table $table): Table { return $table - ->searchable(false) + ->searchable(true) ->defaultPaginationPageOption(25) ->checkIfRecordIsSelectableUsing(fn (Egg $egg) => $egg->servers_count <= 0) ->columns([ Tables\Columns\TextColumn::make('id') ->label('Id') - ->hidden() - ->searchable(), + ->searchable() + ->sortable(), Tables\Columns\TextColumn::make('name') ->icon('tabler-egg') ->description(fn ($record): ?string => (strlen($record->description) > 120) ? substr($record->description, 0, 120).'...' : $record->description) ->wrap() - ->searchable(), + ->searchable() + ->sortable(), Tables\Columns\TextColumn::make('servers_count') ->counts('servers') ->icon('tabler-server') From 60c5f826d6df2d171ecff3a2dbf005839f17cf38 Mon Sep 17 00:00:00 2001 From: kubi Date: Mon, 10 Jun 2024 14:32:36 -0700 Subject: [PATCH 02/27] Run build and tests on commits --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1e4cdc0fd..fe7edecfb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,9 @@ name: Tests on: + push: + branches: + - '**' pull_request: branches: - '**' From 3cadbbc60c4e719d18135a4f97d6027aa0825bff Mon Sep 17 00:00:00 2001 From: kubi Date: Mon, 10 Jun 2024 14:41:02 -0700 Subject: [PATCH 03/27] Run build on commits --- .github/workflows/build.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 80304f9f6..3021c6799 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,6 +1,9 @@ name: Build on: + push: + branches: + - '**' pull_request: branches: - '**' From 6295ea34de363399bdb1df2390a7c18d211e7076 Mon Sep 17 00:00:00 2001 From: kubi Date: Mon, 10 Jun 2024 14:45:32 -0700 Subject: [PATCH 04/27] Update tests to use mariadb 10.3 Update version of mariadb used by tests since this is our minimum required version. --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fe7edecfb..b02b9c3c5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: php: [8.2, 8.3] - database: ["mariadb:10.2", "mysql:8"] + database: ["mariadb:10.3", "mysql:8"] services: database: image: ${{ matrix.database }} From 3ac23d15140ca4cc9f7d094e166c491862251947 Mon Sep 17 00:00:00 2001 From: kubi Date: Mon, 10 Jun 2024 14:55:14 -0700 Subject: [PATCH 05/27] Update version of upload release actions in release workflow --- .github/workflows/release.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4a3bcf5ab..c2af91aff 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -54,7 +54,7 @@ jobs: - name: Create release id: create_release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -63,7 +63,7 @@ jobs: - name: Upload release archive id: upload-release-archive - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -74,7 +74,7 @@ jobs: - name: Upload release checksum id: upload-release-checksum - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 918444176344f57cb36150615af61e10ccfcfd95 Mon Sep 17 00:00:00 2001 From: kubi Date: Mon, 10 Jun 2024 15:08:18 -0700 Subject: [PATCH 06/27] Update release workflow for new parameter format (#363) action-gh-release combined their artifact uploader action with their create release action. Combine these into a single step. --- .github/workflows/release.yaml | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c2af91aff..5ede81c07 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -60,25 +60,6 @@ jobs: with: draft: true prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }} - - - name: Upload release archive - id: upload-release-archive - uses: actions/upload-release-asset@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: panel.tar.gz - asset_name: panel.tar.gz - asset_content_type: application/gzip - - - name: Upload release checksum - id: upload-release-checksum - uses: actions/upload-release-asset@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./checksum.txt - asset_name: checksum.txt - asset_content_type: text/plain + files: | + panel.tar.gz + checksum.txt From beac4cd3f67180434f3041842e31de8eff84d3b9 Mon Sep 17 00:00:00 2001 From: MartinOscar <40749467+RMartinOscar@users.noreply.github.com> Date: Tue, 11 Jun 2024 18:21:13 +0200 Subject: [PATCH 07/27] Update AllocationsRelationManager.php (#369) * Update AllocationsRelationManager.php * Update app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php --------- Co-authored-by: Lance Pioch --- .../RelationManagers/AllocationsRelationManager.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php index ef200f841..f698a393e 100644 --- a/app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php +++ b/app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php @@ -68,7 +68,6 @@ class AllocationsRelationManager extends RelationManager ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DissociateBulkAction::make(), - Tables\Actions\DeleteBulkAction::make(), ]), ]); } From 8e5660a1b90f9756e72ed845e39661177c4587fa Mon Sep 17 00:00:00 2001 From: MartinOscar <40749467+RMartinOscar@users.noreply.github.com> Date: Tue, 11 Jun 2024 18:21:51 +0200 Subject: [PATCH 08/27] Add default for Automatic Allocation in CreateNode (#367) * Update CreateNode.php * Update app/Filament/Resources/NodeResource/Pages/CreateNode.php Co-authored-by: Boy132 --- app/Filament/Resources/NodeResource/Pages/CreateNode.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Filament/Resources/NodeResource/Pages/CreateNode.php b/app/Filament/Resources/NodeResource/Pages/CreateNode.php index 50cda02b2..bd21dc558 100644 --- a/app/Filament/Resources/NodeResource/Pages/CreateNode.php +++ b/app/Filament/Resources/NodeResource/Pages/CreateNode.php @@ -212,6 +212,7 @@ class CreateNode extends CreateRecord false => 'success', ]), Forms\Components\ToggleButtons::make('public') + ->default(true) ->columnSpan(1) ->label('Automatic Allocation')->inline() ->options([ From 58bfa122802b7612e7a7de9df99d144a66083647 Mon Sep 17 00:00:00 2001 From: MartinOscar <40749467+RMartinOscar@users.noreply.github.com> Date: Tue, 11 Jun 2024 18:22:37 +0200 Subject: [PATCH 09/27] Cleanup unused vars (#366) * Update app/Exceptions/Handler.php Co-authored-by: Boy132 --- app/Exceptions/Handler.php | 2 +- .../EggResource/RelationManagers/ServersRelationManager.php | 2 +- .../NodeResource/RelationManagers/NodesRelationManager.php | 2 +- app/Filament/Resources/ServerResource/Pages/ListServers.php | 2 +- .../UserResource/RelationManagers/ServersRelationManager.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 38f49c8ff..d30475f66 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -215,7 +215,7 @@ class Handler extends ExceptionHandler ->map(fn ($trace) => Arr::except($trace, ['args'])) ->all(), 'previous' => Collection::make($this->extractPrevious($e)) - ->map(fn ($exception) => $e->getTrace()) + ->map(fn ($exception) => $exception->getTrace()) ->map(fn ($trace) => Arr::except($trace, ['args'])) ->all(), ], diff --git a/app/Filament/Resources/EggResource/RelationManagers/ServersRelationManager.php b/app/Filament/Resources/EggResource/RelationManagers/ServersRelationManager.php index 3e72e4236..cffaa822b 100644 --- a/app/Filament/Resources/EggResource/RelationManagers/ServersRelationManager.php +++ b/app/Filament/Resources/EggResource/RelationManagers/ServersRelationManager.php @@ -34,7 +34,7 @@ class ServersRelationManager extends RelationManager ->label('Docker Image'), Tables\Columns\SelectColumn::make('allocation.id') ->label('Primary Allocation') - ->options(fn ($state, Server $server) => [$server->allocation->id => $server->allocation->address]) + ->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address]) ->selectablePlaceholder(false) ->sortable(), ]); diff --git a/app/Filament/Resources/NodeResource/RelationManagers/NodesRelationManager.php b/app/Filament/Resources/NodeResource/RelationManagers/NodesRelationManager.php index b9775b4c2..4e3081f08 100644 --- a/app/Filament/Resources/NodeResource/RelationManagers/NodesRelationManager.php +++ b/app/Filament/Resources/NodeResource/RelationManagers/NodesRelationManager.php @@ -34,7 +34,7 @@ class NodesRelationManager extends RelationManager ->sortable(), Tables\Columns\SelectColumn::make('allocation.id') ->label('Primary Allocation') - ->options(fn ($state, Server $server) => [$server->allocation->id => $server->allocation->address]) + ->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address]) ->selectablePlaceholder(false) ->sortable(), Tables\Columns\TextColumn::make('memory')->icon('tabler-device-desktop-analytics'), diff --git a/app/Filament/Resources/ServerResource/Pages/ListServers.php b/app/Filament/Resources/ServerResource/Pages/ListServers.php index 0f150144d..00fff906e 100644 --- a/app/Filament/Resources/ServerResource/Pages/ListServers.php +++ b/app/Filament/Resources/ServerResource/Pages/ListServers.php @@ -70,7 +70,7 @@ class ListServers extends ListRecords ->sortable(), Tables\Columns\SelectColumn::make('allocation_id') ->label('Primary Allocation') - ->options(fn ($state, Server $server) => $server->allocations->mapWithKeys( + ->options(fn (Server $server) => $server->allocations->mapWithKeys( fn ($allocation) => [$allocation->id => $allocation->address]) ) ->selectablePlaceholder(false) diff --git a/app/Filament/Resources/UserResource/RelationManagers/ServersRelationManager.php b/app/Filament/Resources/UserResource/RelationManagers/ServersRelationManager.php index 7be484b60..ce198e2d0 100644 --- a/app/Filament/Resources/UserResource/RelationManagers/ServersRelationManager.php +++ b/app/Filament/Resources/UserResource/RelationManagers/ServersRelationManager.php @@ -68,7 +68,7 @@ class ServersRelationManager extends RelationManager ->sortable(), Tables\Columns\SelectColumn::make('allocation.id') ->label('Primary Allocation') - ->options(fn ($state, Server $server) => [$server->allocation->id => $server->allocation->address]) + ->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address]) ->selectablePlaceholder(false) ->sortable(), Tables\Columns\TextColumn::make('image')->hidden(), From 6d6b50c27d893eac1fb7ad725645e8c9a82d4255 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Tue, 11 Jun 2024 12:48:04 -0400 Subject: [PATCH 10/27] Create bounties.md --- bounties.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 bounties.md diff --git a/bounties.md b/bounties.md new file mode 100644 index 000000000..0bac2c001 --- /dev/null +++ b/bounties.md @@ -0,0 +1,20 @@ +# [Bounties](https://github.com/pelican-dev/panel/issues?q=is%3Aopen+is%3Aissue+label%3A%22%F0%9F%92%B5+bounty%22) + +Get paid to improve Pelican! + +## Rules + +* code must be merged into the main branch +* bounty eligibility is solely at our discretion +* open a ticket at [hub.pelican.dev](https://hub.pelican.dev/tickets) with links to your PRs to claim +* get an extra 25% if you redeem your bounty in Donor credit +* for bounties >$100, the first PR gets a lock, which times out after a week of no progress + +We put up each bounty with the intention that it'll get merged, but occasionally the right resolution is to close the bounty, which only becomes clear once some effort is put in. +This is still valuable work, so we'll pay out $50 for getting any bounty closed with a good explanation. + +## Issue bounties + +We've tagged bounty-eligible issues across openpilot and the rest of our repos; check out all the open ones [here](https://github.com/pelican-dev/panel/issues?q=is%3Aopen+is%3Aissue+label%3A%22%F0%9F%92%B5+bounty%22). + +New bounties can be proposed in the [**#feedback**](https://discord.com/channels/1218730176297439332/1218732581797892220) channel in Discord. From f21637626537e86d701fec12c40ccf1524a65b79 Mon Sep 17 00:00:00 2001 From: MartinOscar <40749467+RMartinOscar@users.noreply.github.com> Date: Tue, 11 Jun 2024 20:32:55 +0200 Subject: [PATCH 11/27] Update EditProfile (#368) * Update EditProfile.php --- app/Filament/Resources/UserResource/Pages/EditProfile.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Filament/Resources/UserResource/Pages/EditProfile.php b/app/Filament/Resources/UserResource/Pages/EditProfile.php index e04179ba8..f59a5b56e 100644 --- a/app/Filament/Resources/UserResource/Pages/EditProfile.php +++ b/app/Filament/Resources/UserResource/Pages/EditProfile.php @@ -193,8 +193,10 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile ->schema([ Grid::make('asdf')->columns(5)->schema([ Section::make('Create API Key')->columnSpan(3)->schema([ - TextInput::make('description')->required(), + TextInput::make('description') + ->live(), TagsInput::make('allowed_ips') + ->live() ->splitKeys([',', ' ', 'Tab']) ->placeholder('Example: 127.0.0.1 or 192.168.1.1') ->label('Whitelisted IP\'s') @@ -202,6 +204,7 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile ->columnSpanFull(), ])->headerActions([ Action::make('Create') + ->disabled(fn (Get $get) => $get('description') === null) ->successRedirectUrl(route('filament.admin.auth.profile', ['tab' => '-api-keys-tab'])) ->action(function (Get $get, Action $action, $user) { $token = $user->createToken( From 46a24a087b124a405795efb2c4f3086c90df6952 Mon Sep 17 00:00:00 2001 From: MartinOscar <40749467+RMartinOscar@users.noreply.github.com> Date: Tue, 11 Jun 2024 20:36:22 +0200 Subject: [PATCH 12/27] Update CreateServer.php (#364) --- app/Filament/Resources/ServerResource/Pages/CreateServer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Filament/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Resources/ServerResource/Pages/CreateServer.php index 2d92ddda7..8928698c2 100644 --- a/app/Filament/Resources/ServerResource/Pages/CreateServer.php +++ b/app/Filament/Resources/ServerResource/Pages/CreateServer.php @@ -192,7 +192,8 @@ class CreateServer extends CreateRecord $start = max((int) $start, 0); $end = min((int) $end, 2 ** 16 - 1); - for ($i = $start; $i <= $end; $i++) { + $range = $start <= $end ? range($start, $end) : range($end, $start); + foreach ($range as $i) { $ports->push($i); } } From e95cd0cd987dc1f13c52ee80dbcbb4166760fb81 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Tue, 11 Jun 2024 14:58:42 -0400 Subject: [PATCH 13/27] Update bounties.md --- bounties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bounties.md b/bounties.md index 0bac2c001..9f9dc890b 100644 --- a/bounties.md +++ b/bounties.md @@ -8,7 +8,7 @@ Get paid to improve Pelican! * bounty eligibility is solely at our discretion * open a ticket at [hub.pelican.dev](https://hub.pelican.dev/tickets) with links to your PRs to claim * get an extra 25% if you redeem your bounty in Donor credit -* for bounties >$100, the first PR gets a lock, which times out after a week of no progress +* for bounties >=$100, the first PR gets a lock, which times out after a week of no progress We put up each bounty with the intention that it'll get merged, but occasionally the right resolution is to close the bounty, which only becomes clear once some effort is put in. This is still valuable work, so we'll pay out $50 for getting any bounty closed with a good explanation. From 1d66d4c3205dba74cf4b1c50b8809ba18b76e1f4 Mon Sep 17 00:00:00 2001 From: Djordy Koert Date: Tue, 11 Jun 2024 21:00:51 +0200 Subject: [PATCH 14/27] PHPStan workflow + PHPStan fixes (#339) * add PHPStan to workflow Co-authored-by: Boy132 Co-authored-by: Djordy Co-authored-by: Lance Pioch --- .github/workflows/lint.yaml | 29 +++++++++++++++++-- .../Resources/EggResource/Pages/ListEggs.php | 1 - .../Resources/NodeResource/Pages/EditNode.php | 9 +++--- .../AllocationsRelationManager.php | 7 +++-- .../ServerResource/Pages/CreateServer.php | 4 +-- .../ServerResource/Pages/EditServer.php | 14 +++++---- .../ServerResource/Pages/ListServers.php | 8 +---- .../AllocationsRelationManager.php | 8 +++-- .../Servers/ServerManagementController.php | 8 ++--- .../Application/Mounts/UpdateMountRequest.php | 5 ++-- app/Models/Allocation.php | 3 +- app/Models/Server.php | 2 +- .../Daemon/DaemonServerRepository.php | 6 ++-- .../Api/Client/ActivityLogTransformer.php | 2 +- composer.json | 1 + 15 files changed, 67 insertions(+), 40 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 3dd538aad..0b767fcf7 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -6,8 +6,8 @@ on: - '**' jobs: - lint: - name: Lint + pint: + name: Pint runs-on: ubuntu-latest steps: - name: Code Checkout @@ -16,7 +16,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: "8.2" + php-version: "8.3" extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip tools: composer:v2 coverage: none @@ -29,3 +29,26 @@ jobs: - name: Pint run: vendor/bin/pint --test + phpstan: + name: PHPStan + runs-on: ubuntu-latest + steps: + - name: Code Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.3" + extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip + tools: composer:v2 + coverage: none + + - name: Setup .env + run: cp .env.example .env + + - name: Install dependencies + run: composer install --no-interaction --no-progress --prefer-dist + + - name: PHPStan + run: vendor/bin/phpstan --memory-limit=-1 diff --git a/app/Filament/Resources/EggResource/Pages/ListEggs.php b/app/Filament/Resources/EggResource/Pages/ListEggs.php index c3119b17d..8cd878a71 100644 --- a/app/Filament/Resources/EggResource/Pages/ListEggs.php +++ b/app/Filament/Resources/EggResource/Pages/ListEggs.php @@ -89,7 +89,6 @@ class ListEggs extends ListRecords ]) ->action(function (array $data): void { - /** @var EggImporterService $eggImportService */ $eggImportService = resolve(EggImporterService::class); diff --git a/app/Filament/Resources/NodeResource/Pages/EditNode.php b/app/Filament/Resources/NodeResource/Pages/EditNode.php index b7236232d..4b7f90ad1 100644 --- a/app/Filament/Resources/NodeResource/Pages/EditNode.php +++ b/app/Filament/Resources/NodeResource/Pages/EditNode.php @@ -395,10 +395,11 @@ class EditNode extends EditRecord ->requiresConfirmation() ->modalHeading('Reset Daemon Token?') ->modalDescription('Resetting the daemon token will void any request coming from the old token. This token is used for all sensitive operations on the daemon including server creation and deletion. We suggest changing this token regularly for security.') - ->action(fn (NodeUpdateService $nodeUpdateService, Node $node) => $nodeUpdateService->handle($node, [], true) - && Notification::make()->success()->title('Daemon Key Reset')->send() - && $this->fillForm() - ), + ->action(function (NodeUpdateService $nodeUpdateService, Node $node) { + $nodeUpdateService->handle($node, [], true); + Notification::make()->success()->title('Daemon Key Reset')->send(); + $this->fillForm(); + }), ]), ]), ]), diff --git a/app/Filament/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php index f74cf1fca..97eb8d37d 100644 --- a/app/Filament/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php +++ b/app/Filament/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php @@ -3,7 +3,7 @@ namespace App\Filament\Resources\NodeResource\RelationManagers; use App\Models\Allocation; -use App\Models\Server; +use App\Models\Node; use App\Services\Allocations\AssignmentService; use Filament\Forms; use Filament\Forms\Form; @@ -12,6 +12,9 @@ use Filament\Tables; use Filament\Tables\Table; use Illuminate\Support\HtmlString; +/** + * @method Node getOwnerRecord() + */ class AllocationsRelationManager extends RelationManager { protected static string $relationship = 'allocations'; @@ -66,7 +69,7 @@ class AllocationsRelationManager extends RelationManager Tables\Actions\Action::make('create new allocation')->label('Create Allocations') ->form(fn () => [ Forms\Components\TextInput::make('allocation_ip') - ->datalist($this->getOwnerRecord()->ipAddresses() ?? []) + ->datalist($this->getOwnerRecord()->ipAddresses()) ->label('IP Address') ->inlineLabel() ->ipv4() diff --git a/app/Filament/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Resources/ServerResource/Pages/CreateServer.php index 8928698c2..d61c141fe 100644 --- a/app/Filament/Resources/ServerResource/Pages/CreateServer.php +++ b/app/Filament/Resources/ServerResource/Pages/CreateServer.php @@ -426,7 +426,6 @@ class CreateServer extends CreateRecord $components = [$text, $select]; - /** @var Forms\Components\Component $component */ foreach ($components as &$component) { $component = $component ->live(onBlur: true) @@ -576,6 +575,7 @@ class CreateServer extends CreateRecord 'unlimited' => -1, 'disabled' => 0, 'limited' => 128, + default => throw new \LogicException('Invalid state'), }; $set('swap', $value); @@ -595,7 +595,7 @@ class CreateServer extends CreateRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => match ($get('swap_support')) { 'disabled', 'unlimited' => true, - 'limited' => false, + default => false, }) ->label('Swap Memory') ->default(0) diff --git a/app/Filament/Resources/ServerResource/Pages/EditServer.php b/app/Filament/Resources/ServerResource/Pages/EditServer.php index e425a0331..c7a3ae4e5 100644 --- a/app/Filament/Resources/ServerResource/Pages/EditServer.php +++ b/app/Filament/Resources/ServerResource/Pages/EditServer.php @@ -2,6 +2,7 @@ namespace App\Filament\Resources\ServerResource\Pages; +use LogicException; use App\Filament\Resources\ServerResource; use App\Http\Controllers\Admin\ServersController; use App\Services\Servers\RandomWordService; @@ -299,6 +300,7 @@ class EditServer extends EditRecord 'unlimited' => -1, 'disabled' => 0, 'limited' => 128, + default => throw new LogicException('Invalid state') }; $set('swap', $value); @@ -308,6 +310,7 @@ class EditServer extends EditRecord $get('swap') > 0 => 'limited', $get('swap') == 0 => 'disabled', $get('swap') < 0 => 'unlimited', + default => throw new LogicException('Invalid state') }; }) ->options([ @@ -325,7 +328,7 @@ class EditServer extends EditRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => match ($get('swap_support')) { 'disabled', 'unlimited', true => true, - 'limited', false => false, + default => false, }) ->label('Swap Memory')->inlineLabel() ->suffix('MiB') @@ -553,7 +556,6 @@ class EditServer extends EditRecord $components = [$text, $select]; - /** @var Forms\Components\Component $component */ foreach ($components as &$component) { $component = $component ->live(onBlur: true) @@ -606,7 +608,7 @@ class EditServer extends EditRecord ->action(function (ServersController $serversController, Server $server) { $serversController->toggleInstall($server); - return $this->refreshFormData(['status', 'docker']); + $this->refreshFormData(['status', 'docker']); }), ])->fullWidth(), Forms\Components\ToggleButtons::make('') @@ -624,7 +626,7 @@ class EditServer extends EditRecord $suspensionService->toggle($server, 'suspend'); Notification::make()->success()->title('Server Suspended!')->send(); - return $this->refreshFormData(['status', 'docker']); + $this->refreshFormData(['status', 'docker']); }), Forms\Components\Actions\Action::make('toggleUnsuspend') ->label('Unsuspend') @@ -634,7 +636,7 @@ class EditServer extends EditRecord $suspensionService->toggle($server, 'unsuspend'); Notification::make()->success()->title('Server Unsuspended!')->send(); - return $this->refreshFormData(['status', 'docker']); + $this->refreshFormData(['status', 'docker']); }), ])->fullWidth(), Forms\Components\ToggleButtons::make('') @@ -650,7 +652,7 @@ class EditServer extends EditRecord Forms\Components\Actions::make([ Forms\Components\Actions\Action::make('transfer') ->label('Transfer Soon™') - ->action(fn (TransferServerService $transfer, Server $server) => $transfer->handle($server, $data)) + ->action(fn (TransferServerService $transfer, Server $server) => $transfer->handle($server, [])) ->disabled() //TODO! ->form([ //TODO! Forms\Components\Select::make('newNode') diff --git a/app/Filament/Resources/ServerResource/Pages/ListServers.php b/app/Filament/Resources/ServerResource/Pages/ListServers.php index 00fff906e..3e20d8830 100644 --- a/app/Filament/Resources/ServerResource/Pages/ListServers.php +++ b/app/Filament/Resources/ServerResource/Pages/ListServers.php @@ -22,13 +22,7 @@ class ListServers extends ListRecords Tables\Columns\TextColumn::make('status') ->default('unknown') ->badge() - ->default(function (Server $server) { - if ($server->status !== null) { - return $server->status; - } - - return $server->retrieveStatus() ?? 'node_fail'; - }) + ->default(fn (Server $server) => $server->status ?? $server->retrieveStatus()) ->icon(fn ($state) => match ($state) { 'node_fail' => 'tabler-server-off', 'running' => 'tabler-heartbeat', diff --git a/app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php index f698a393e..d2615a481 100644 --- a/app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php +++ b/app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php @@ -3,12 +3,16 @@ namespace App\Filament\Resources\ServerResource\RelationManagers; use App\Models\Allocation; +use App\Models\Server; use Filament\Forms; use Filament\Forms\Form; use Filament\Resources\RelationManagers\RelationManager; use Filament\Tables; use Filament\Tables\Table; +/** + * @method Server getOwnerRecord() + */ class AllocationsRelationManager extends RelationManager { protected static string $relationship = 'allocations'; @@ -38,12 +42,12 @@ class AllocationsRelationManager extends RelationManager Tables\Columns\TextInputColumn::make('ip_alias')->label('Alias'), Tables\Columns\IconColumn::make('primary') ->icon(fn ($state) => match ($state) { - false => 'tabler-star', true => 'tabler-star-filled', + default => 'tabler-star', }) ->color(fn ($state) => match ($state) { - false => 'gray', true => 'warning', + default => 'gray', }) ->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id])) ->default(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id) diff --git a/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php b/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php index d728ea83c..4c9b512ae 100644 --- a/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php +++ b/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php @@ -75,11 +75,11 @@ class ServerManagementController extends ApplicationApiController if ($this->transferServerService->handle($server, $validatedData)) { // Transfer started - $this->returnNoContent(); - } else { - // Node was not viable - return new Response('', Response::HTTP_NOT_ACCEPTABLE); + return $this->returnNoContent(); } + + // Node was not viable + return new Response('', Response::HTTP_NOT_ACCEPTABLE); } /** diff --git a/app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php b/app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php index 213507fca..7f76ec420 100644 --- a/app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php +++ b/app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php @@ -7,14 +7,13 @@ use App\Models\Mount; class UpdateMountRequest extends StoreMountRequest { /** - * Apply validation rules to this request. Uses the parent class rules() - * function but passes in the rules for updating rather than creating. + * Apply validation rules to this request. */ public function rules(array $rules = null): array { /** @var Mount $mount */ $mount = $this->route()->parameter('mount'); - return parent::rules(Mount::getRulesForUpdate($mount->id)); + return Mount::getRulesForUpdate($mount->id); } } diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index 33bc7f16d..abd60bfba 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -103,7 +103,8 @@ class Allocation extends Model return !is_null($this->ip_alias); } - public function address(): Attribute + /** @return Attribute */ + protected function address(): Attribute { return Attribute::make( get: fn () => "$this->ip:$this->port", diff --git a/app/Models/Server.php b/app/Models/Server.php index 9b5e5b1cc..5aca895dd 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -26,7 +26,7 @@ use App\Exceptions\Http\Server\ServerStateConflictException; * @property int $node_id * @property string $name * @property string $description - * @property string|null $status + * @property ServerState|null $status * @property bool $skip_scripts * @property int $owner_id * @property int $memory diff --git a/app/Repositories/Daemon/DaemonServerRepository.php b/app/Repositories/Daemon/DaemonServerRepository.php index 18c78f7fd..ce91ece2b 100644 --- a/app/Repositories/Daemon/DaemonServerRepository.php +++ b/app/Repositories/Daemon/DaemonServerRepository.php @@ -25,9 +25,9 @@ class DaemonServerRepository extends DaemonRepository Assert::isInstanceOf($this->server, Server::class); try { - $response = $this->getHttpClient()->get( + return $this->getHttpClient()->get( sprintf('/api/servers/%s', $this->server->uuid) - )->throw(); + )->throw()->json(); } catch (RequestException $exception) { $cfId = $exception->response->header('Cf-Ray'); $cfCache = $exception->response->header('Cf-Cache-Status'); @@ -48,7 +48,7 @@ class DaemonServerRepository extends DaemonRepository report($exception); } - return $response?->json() ?? ['state' => ContainerStatus::Missing->value]; + return ['state' => ContainerStatus::Missing->value]; } /** diff --git a/app/Transformers/Api/Client/ActivityLogTransformer.php b/app/Transformers/Api/Client/ActivityLogTransformer.php index d02f95988..af666beb6 100644 --- a/app/Transformers/Api/Client/ActivityLogTransformer.php +++ b/app/Transformers/Api/Client/ActivityLogTransformer.php @@ -55,7 +55,7 @@ class ActivityLogTransformer extends BaseClientTransformer $properties = $model->properties ->mapWithKeys(function ($value, $key) use ($model) { - if ($key === 'ip' && $model->actor && !$model->actor->is($this->request->user())) { + if ($key === 'ip' && !$model->actor->is($this->request->user())) { return [$key => '[hidden]']; } diff --git a/composer.json b/composer.json index 399b42278..833cef865 100644 --- a/composer.json +++ b/composer.json @@ -70,6 +70,7 @@ "scripts": { "cs:fix": "php-cs-fixer fix", "cs:check": "php-cs-fixer fix --dry-run --diff --verbose", + "phpstan": "phpstan --memory-limit=-1", "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump" ], From 10796f8916abb6536e81cc7f9c02f4caca1a42f5 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Tue, 11 Jun 2024 21:01:14 +0200 Subject: [PATCH 15/27] Dedicated MariaDB driver (#365) * dedicated tests for mariadb * fix migrations * update database config * update database setup command --- .github/workflows/ci.yaml | 74 ++++++++++++- .../Environment/DatabaseSettingsCommand.php | 104 +++++++++++++----- config/database.php | 34 ++++-- ...5_28_135717_create_activity_logs_table.php | 2 +- 4 files changed, 177 insertions(+), 37 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b02b9c3c5..43a644a99 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: php: [8.2, 8.3] - database: ["mariadb:10.3", "mysql:8"] + database: ["mysql:8"] services: database: image: ${{ matrix.database }} @@ -81,6 +81,78 @@ jobs: DB_PORT: ${{ job.services.database.ports[3306] }} DB_USERNAME: root + mariadb: + name: MariaDB + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: [8.2, 8.3] + database: ["mariadb:10.3", "mariadb:10.11", "mariadb:11.4"] + services: + database: + image: ${{ matrix.database }} + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: testing + ports: + - 3306 + options: --health-cmd="mariadb-admin ping || mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + env: + APP_ENV: testing + APP_DEBUG: "false" + APP_KEY: ThisIsARandomStringForTests12345 + APP_TIMEZONE: UTC + APP_URL: http://localhost/ + APP_ENVIRONMENT_ONLY: "true" + CACHE_DRIVER: array + MAIL_MAILER: array + SESSION_DRIVER: array + QUEUE_CONNECTION: sync + DB_CONNECTION: mariadb + DB_HOST: 127.0.0.1 + DB_DATABASE: testing + DB_USERNAME: root + steps: + - name: Code Checkout + uses: actions/checkout@v4 + + - name: Get cache directory + id: composer-cache + run: | + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-${{ matrix.php }}- + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip + tools: composer:v2 + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --no-suggest --prefer-dist + + - name: Unit tests + run: vendor/bin/phpunit tests/Unit + env: + DB_HOST: UNIT_NO_DB + SKIP_MIGRATIONS: true + + - name: Integration tests + run: vendor/bin/phpunit tests/Integration + env: + DB_PORT: ${{ job.services.database.ports[3306] }} + DB_USERNAME: root + sqlite: name: SQLite runs-on: ubuntu-latest diff --git a/app/Console/Commands/Environment/DatabaseSettingsCommand.php b/app/Console/Commands/Environment/DatabaseSettingsCommand.php index bc71a3419..f05f9bbc4 100644 --- a/app/Console/Commands/Environment/DatabaseSettingsCommand.php +++ b/app/Console/Commands/Environment/DatabaseSettingsCommand.php @@ -13,6 +13,7 @@ class DatabaseSettingsCommand extends Command public const DATABASE_DRIVERS = [ 'sqlite' => 'SQLite (recommended)', + 'mariadb' => 'MariaDB', 'mysql' => 'MySQL', ]; @@ -21,10 +22,10 @@ class DatabaseSettingsCommand extends Command protected $signature = 'p:environment:database {--driver= : The database driver backend to use.} {--database= : The database to use.} - {--host= : The connection address for the MySQL server.} - {--port= : The connection port for the MySQL server.} - {--username= : Username to use when connecting to the MySQL server.} - {--password= : Password to use for the MySQL database.}'; + {--host= : The connection address for the MySQL/ MariaDB server.} + {--port= : The connection port for the MySQL/ MariaDB server.} + {--username= : Username to use when connecting to the MySQL/ MariaDB server.} + {--password= : Password to use for the MySQL/ MariaDB database.}'; protected array $variables = []; @@ -82,7 +83,20 @@ class DatabaseSettingsCommand extends Command } try { - $this->testMySQLConnection(); + // Test connection + config()->set('database.connections._panel_command_test', [ + 'driver' => 'mysql', + 'host' => $this->variables['DB_HOST'], + 'port' => $this->variables['DB_PORT'], + 'database' => $this->variables['DB_DATABASE'], + 'username' => $this->variables['DB_USERNAME'], + 'password' => $this->variables['DB_PASSWORD'], + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'strict' => true, + ]); + + $this->database->connection('_panel_command_test')->getPdo(); } catch (\PDOException $exception) { $this->output->error(sprintf('Unable to connect to the MySQL server using the provided credentials. The error returned was "%s".', $exception->getMessage())); $this->output->error(__('commands.database_settings.DB_error_2')); @@ -93,6 +107,66 @@ class DatabaseSettingsCommand extends Command return $this->handle(); } + return 1; + } + } elseif ($this->variables['DB_CONNECTION'] === 'mariadb') { + $this->output->note(__('commands.database_settings.DB_HOST_note')); + $this->variables['DB_HOST'] = $this->option('host') ?? $this->ask( + 'Database Host', + config('database.connections.mariadb.host', '127.0.0.1') + ); + + $this->variables['DB_PORT'] = $this->option('port') ?? $this->ask( + 'Database Port', + config('database.connections.mariadb.port', 3306) + ); + + $this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask( + 'Database Name', + config('database.connections.mariadb.database', 'panel') + ); + + $this->output->note(__('commands.database_settings.DB_USERNAME_note')); + $this->variables['DB_USERNAME'] = $this->option('username') ?? $this->ask( + 'Database Username', + config('database.connections.mariadb.username', 'pelican') + ); + + $askForMariaDBPassword = true; + if (!empty(config('database.connections.mariadb.password')) && $this->input->isInteractive()) { + $this->variables['DB_PASSWORD'] = config('database.connections.mariadb.password'); + $askForMariaDBPassword = $this->confirm(__('commands.database_settings.DB_PASSWORD_note')); + } + + if ($askForMariaDBPassword) { + $this->variables['DB_PASSWORD'] = $this->option('password') ?? $this->secret('Database Password'); + } + + try { + // Test connection + config()->set('database.connections._panel_command_test', [ + 'driver' => 'mariadb', + 'host' => $this->variables['DB_HOST'], + 'port' => $this->variables['DB_PORT'], + 'database' => $this->variables['DB_DATABASE'], + 'username' => $this->variables['DB_USERNAME'], + 'password' => $this->variables['DB_PASSWORD'], + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'strict' => true, + ]); + + $this->database->connection('_panel_command_test')->getPdo(); + } catch (\PDOException $exception) { + $this->output->error(sprintf('Unable to connect to the MariaDB server using the provided credentials. The error returned was "%s".', $exception->getMessage())); + $this->output->error(__('commands.database_settings.DB_error_2')); + + if ($this->confirm(__('commands.database_settings.go_back'))) { + $this->database->disconnect('_panel_command_test'); + + return $this->handle(); + } + return 1; } } elseif ($this->variables['DB_CONNECTION'] === 'sqlite') { @@ -108,24 +182,4 @@ class DatabaseSettingsCommand extends Command return 0; } - - /** - * Test that we can connect to the provided MySQL instance and perform a selection. - */ - private function testMySQLConnection() - { - config()->set('database.connections._panel_command_test', [ - 'driver' => 'mysql', - 'host' => $this->variables['DB_HOST'], - 'port' => $this->variables['DB_PORT'], - 'database' => $this->variables['DB_DATABASE'], - 'username' => $this->variables['DB_USERNAME'], - 'password' => $this->variables['DB_PASSWORD'], - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'strict' => true, - ]); - - $this->database->connection('_panel_command_test')->getPdo(); - } } diff --git a/config/database.php b/config/database.php index 79f581bf9..016d1a926 100644 --- a/config/database.php +++ b/config/database.php @@ -1,7 +1,5 @@ env('DB_CONNECTION', 'sqlite'), @@ -17,25 +15,41 @@ return [ 'mysql' => [ 'driver' => 'mysql', - 'url' => env('DB_URL', env('DATABASE_URL')), + 'url' => env('DB_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'panel'), 'username' => env('DB_USERNAME', 'pelican'), 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 'prefix' => env('DB_PREFIX', ''), 'prefix_indexes' => true, 'strict' => env('DB_STRICT_MODE', false), - 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE', 'UTC'))), - 'sslmode' => env('DB_SSLMODE', 'prefer'), + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'panel'), + 'username' => env('DB_USERNAME', 'pelican'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => env('DB_PREFIX', ''), + 'prefix_indexes' => true, + 'strict' => env('DB_STRICT_MODE', false), + 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), - PDO::MYSQL_ATTR_SSL_CERT => env('MYSQL_ATTR_SSL_CERT'), - PDO::MYSQL_ATTR_SSL_KEY => env('MYSQL_ATTR_SSL_KEY'), - PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => env('MYSQL_ATTR_SSL_VERIFY_SERVER_CERT', true), ]) : [], ], ], diff --git a/database/migrations/2022_05_28_135717_create_activity_logs_table.php b/database/migrations/2022_05_28_135717_create_activity_logs_table.php index 066b2a85c..186125aa3 100644 --- a/database/migrations/2022_05_28_135717_create_activity_logs_table.php +++ b/database/migrations/2022_05_28_135717_create_activity_logs_table.php @@ -13,7 +13,7 @@ return new class extends Migration { Schema::create('activity_logs', function (Blueprint $table) { $table->id(); - $table->uuid('batch')->nullable(); + $table->char('batch', 36)->nullable(); $table->string('event')->index(); $table->string('ip'); $table->text('description')->nullable(); From 61f3e965ba737e46ce2db60492bcc7d80891e97b Mon Sep 17 00:00:00 2001 From: Boy132 Date: Tue, 11 Jun 2024 21:01:31 +0200 Subject: [PATCH 16/27] Refactor egg services (#358) * combine importer and updateimport * integrate egg parser into importer * remove EggCreationService and EggUpdateService * run pint * revert change to composer.json * use egg exporter directly instead of old admin route --- .../Resources/EggResource/Pages/EditEgg.php | 4 +- .../Controllers/Admin/Eggs/EggController.php | 25 +++- .../Admin/Eggs/EggShareController.php | 4 +- app/Services/Eggs/EggCreationService.php | 29 ---- app/Services/Eggs/EggParserService.php | 108 --------------- app/Services/Eggs/EggUpdateService.php | 26 ---- .../Eggs/Sharing/EggImporterService.php | 130 ++++++++++++++++-- .../Eggs/Sharing/EggUpdateImporterService.php | 67 --------- composer.json | 2 +- database/Seeders/EggSeeder.php | 9 +- 10 files changed, 143 insertions(+), 261 deletions(-) delete mode 100644 app/Services/Eggs/EggCreationService.php delete mode 100644 app/Services/Eggs/EggParserService.php delete mode 100644 app/Services/Eggs/EggUpdateService.php delete mode 100644 app/Services/Eggs/Sharing/EggUpdateImporterService.php diff --git a/app/Filament/Resources/EggResource/Pages/EditEgg.php b/app/Filament/Resources/EggResource/Pages/EditEgg.php index f0ff69ce1..8f7fdca98 100644 --- a/app/Filament/Resources/EggResource/Pages/EditEgg.php +++ b/app/Filament/Resources/EggResource/Pages/EditEgg.php @@ -7,6 +7,7 @@ use App\Models\Egg; use Filament\Actions; use Filament\Resources\Pages\EditRecord; use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor; +use App\Services\Eggs\Sharing\EggExporterService; use Filament\Forms; use Filament\Forms\Form; @@ -205,8 +206,7 @@ class EditEgg extends EditRecord ->icon('tabler-download') ->label('Export Egg') ->color('primary') - // TODO uses old admin panel export service - ->url(fn (Egg $egg): string => route('admin.eggs.export', ['egg' => $egg['id']])), + ->url(fn (EggExporterService $service, Egg $egg) => $service->handle($egg->id)), $this->getSaveFormAction()->formId('form'), ]; } diff --git a/app/Http/Controllers/Admin/Eggs/EggController.php b/app/Http/Controllers/Admin/Eggs/EggController.php index 5485d1235..692ff7942 100644 --- a/app/Http/Controllers/Admin/Eggs/EggController.php +++ b/app/Http/Controllers/Admin/Eggs/EggController.php @@ -2,15 +2,15 @@ namespace App\Http\Controllers\Admin\Eggs; +use App\Exceptions\Service\Egg\NoParentConfigurationFoundException; use Illuminate\View\View; use App\Models\Egg; use Illuminate\Http\RedirectResponse; use Prologue\Alerts\AlertsMessageBag; use Illuminate\View\Factory as ViewFactory; use App\Http\Controllers\Controller; -use App\Services\Eggs\EggUpdateService; -use App\Services\Eggs\EggCreationService; use App\Http\Requests\Admin\Egg\EggFormRequest; +use Ramsey\Uuid\Uuid; class EggController extends Controller { @@ -19,8 +19,6 @@ class EggController extends Controller */ public function __construct( protected AlertsMessageBag $alert, - protected EggCreationService $creationService, - protected EggUpdateService $updateService, protected ViewFactory $view ) { } @@ -58,7 +56,16 @@ class EggController extends Controller $data['docker_images'] = $this->normalizeDockerImages($data['docker_images'] ?? null); $data['author'] = $request->user()->email; - $egg = $this->creationService->handle($data); + $data['config_from'] = array_get($data, 'config_from'); + if (!is_null($data['config_from'])) { + $parentEgg = Egg::query()->find(array_get($data, 'config_from')); + throw_unless($parentEgg, new NoParentConfigurationFoundException(trans('exceptions.egg.invalid_copy_id'))); + } + + $egg = Egg::query()->create(array_merge($data, [ + 'uuid' => Uuid::uuid4()->toString(), + ])); + $this->alert->success(trans('admin/eggs.notices.egg_created'))->flash(); return redirect()->route('admin.eggs.view', $egg->id); @@ -90,7 +97,13 @@ class EggController extends Controller $data = $request->validated(); $data['docker_images'] = $this->normalizeDockerImages($data['docker_images'] ?? null); - $this->updateService->handle($egg, $data); + $eggId = array_get($data, 'config_from'); + $copiedFromEgg = Egg::query()->find($eggId); + + throw_unless($copiedFromEgg, new NoParentConfigurationFoundException(trans('exceptions.egg.invalid_copy_id'))); + + $egg->update($data); + $this->alert->success(trans('admin/eggs.notices.updated'))->flash(); return redirect()->route('admin.eggs.view', $egg->id); diff --git a/app/Http/Controllers/Admin/Eggs/EggShareController.php b/app/Http/Controllers/Admin/Eggs/EggShareController.php index e938403db..993064b65 100644 --- a/app/Http/Controllers/Admin/Eggs/EggShareController.php +++ b/app/Http/Controllers/Admin/Eggs/EggShareController.php @@ -10,7 +10,6 @@ use Symfony\Component\HttpFoundation\Response; use App\Services\Eggs\Sharing\EggExporterService; use App\Services\Eggs\Sharing\EggImporterService; use App\Http\Requests\Admin\Egg\EggImportFormRequest; -use App\Services\Eggs\Sharing\EggUpdateImporterService; class EggShareController extends Controller { @@ -21,7 +20,6 @@ class EggShareController extends Controller protected AlertsMessageBag $alert, protected EggExporterService $exporterService, protected EggImporterService $importerService, - protected EggUpdateImporterService $updateImporterService ) { } @@ -61,7 +59,7 @@ class EggShareController extends Controller */ public function update(EggImportFormRequest $request, Egg $egg): RedirectResponse { - $this->updateImporterService->fromFile($egg, $request->file('import_file')); + $this->importerService->fromFile($request->file('import_file'), $egg); $this->alert->success(trans('admin/eggs.notices.updated_via_import'))->flash(); return redirect()->route('admin.eggs.view', ['egg' => $egg]); diff --git a/app/Services/Eggs/EggCreationService.php b/app/Services/Eggs/EggCreationService.php deleted file mode 100644 index e4ab3dde5..000000000 --- a/app/Services/Eggs/EggCreationService.php +++ /dev/null @@ -1,29 +0,0 @@ -find(array_get($data, 'config_from')); - throw_unless($parentEgg, new NoParentConfigurationFoundException(trans('exceptions.egg.invalid_copy_id'))); - } - - return Egg::query()->create(array_merge($data, [ - 'uuid' => Uuid::uuid4()->toString(), - ])); - } -} diff --git a/app/Services/Eggs/EggParserService.php b/app/Services/Eggs/EggParserService.php deleted file mode 100644 index 22025dc5c..000000000 --- a/app/Services/Eggs/EggParserService.php +++ /dev/null @@ -1,108 +0,0 @@ - 'server.allocations.default.ip', - 'server.build.default.ip' => 'server.allocations.default.ip', - 'server.build.env.SERVER_PORT' => 'server.allocations.default.port', - 'server.build.default.port' => 'server.allocations.default.port', - 'server.build.env.SERVER_MEMORY' => 'server.build.memory_limit', - 'server.build.memory' => 'server.build.memory_limit', - 'server.build.env.' => 'server.environment.', - 'server.build.environment.' => 'server.environment.', - ]; - - /** - * Takes an uploaded file and parses out the egg configuration from within. - * - * @throws \JsonException - * @throws \App\Exceptions\Service\InvalidFileUploadException - */ - public function handle(UploadedFile $file): array - { - if ($file->getError() !== UPLOAD_ERR_OK) { - throw new InvalidFileUploadException('The selected file was not uploaded successfully'); - } - - $parsed = json_decode($file->getContent(), true, 512, JSON_THROW_ON_ERROR); - - $version = $parsed['meta']['version'] ?? ''; - - $parsed = match ($version) { - 'PTDL_v1' => $this->convertToV2($parsed), - 'PTDL_v2' => $parsed, - default => throw new InvalidFileUploadException('The JSON file provided is not in a format that can be recognized.') - }; - - // Make sure we only use recent variable format from now on - $parsed['config']['files'] = str_replace( - array_keys(self::UPGRADE_VARIABLES), - array_values(self::UPGRADE_VARIABLES), - $parsed['config']['files'] ?? '', - ); - - return $parsed; - } - - /** - * Fills the provided model with the parsed JSON data. - */ - public function fillFromParsed(Egg $model, array $parsed): Egg - { - return $model->forceFill([ - 'name' => Arr::get($parsed, 'name'), - 'description' => Arr::get($parsed, 'description'), - 'features' => Arr::get($parsed, 'features'), - 'docker_images' => Arr::get($parsed, 'docker_images'), - 'file_denylist' => Collection::make(Arr::get($parsed, 'file_denylist')) - ->filter(fn ($value) => !empty($value)), - 'update_url' => Arr::get($parsed, 'meta.update_url'), - 'config_files' => Arr::get($parsed, 'config.files'), - 'config_startup' => Arr::get($parsed, 'config.startup'), - 'config_logs' => Arr::get($parsed, 'config.logs'), - 'config_stop' => Arr::get($parsed, 'config.stop'), - 'startup' => Arr::get($parsed, 'startup'), - 'script_install' => Arr::get($parsed, 'scripts.installation.script'), - 'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'), - 'script_container' => Arr::get($parsed, 'scripts.installation.container'), - ]); - } - - /** - * Converts a PTDL_V1 egg into the expected PTDL_V2 egg format. This just handles - * the "docker_images" field potentially not being present, and not being in the - * expected "key => value" format. - */ - protected function convertToV2(array $parsed): array - { - // Maintain backwards compatability for eggs that are still using the old single image - // string format. New eggs can provide an array of Docker images that can be used. - if (!isset($parsed['images'])) { - $images = [Arr::get($parsed, 'image') ?? 'nil']; - } else { - $images = $parsed['images']; - } - - unset($parsed['images'], $parsed['image']); - - $parsed['docker_images'] = []; - foreach ($images as $image) { - $parsed['docker_images'][$image] = $image; - } - - $parsed['variables'] = array_map(function ($value) { - return array_merge($value, ['field_type' => 'text']); - }, $parsed['variables']); - - return $parsed; - } -} diff --git a/app/Services/Eggs/EggUpdateService.php b/app/Services/Eggs/EggUpdateService.php deleted file mode 100644 index 5591b3181..000000000 --- a/app/Services/Eggs/EggUpdateService.php +++ /dev/null @@ -1,26 +0,0 @@ -find($eggId); - - throw_unless($copiedFromEgg, new NoParentConfigurationFoundException(trans('exceptions.egg.invalid_copy_id'))); - - // TODO: Once the admin UI is done being reworked and this is exposed - // in said UI, remove this so that you can actually update the denylist. - unset($data['file_denylist']); - - $egg->update($data); - } -} diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php index 5c94ec2fa..1e85923d7 100644 --- a/app/Services/Eggs/Sharing/EggImporterService.php +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -2,18 +2,30 @@ namespace App\Services\Eggs\Sharing; +use App\Exceptions\Service\InvalidFileUploadException; use Ramsey\Uuid\Uuid; use Illuminate\Support\Arr; use App\Models\Egg; use Illuminate\Http\UploadedFile; use App\Models\EggVariable; use Illuminate\Database\ConnectionInterface; -use App\Services\Eggs\EggParserService; +use Illuminate\Support\Collection; use Spatie\TemporaryDirectory\TemporaryDirectory; class EggImporterService { - public function __construct(protected ConnectionInterface $connection, protected EggParserService $parser) + public const UPGRADE_VARIABLES = [ + 'server.build.env.SERVER_IP' => 'server.allocations.default.ip', + 'server.build.default.ip' => 'server.allocations.default.ip', + 'server.build.env.SERVER_PORT' => 'server.allocations.default.port', + 'server.build.default.port' => 'server.allocations.default.port', + 'server.build.env.SERVER_MEMORY' => 'server.build.memory_limit', + 'server.build.memory' => 'server.build.memory_limit', + 'server.build.env.' => 'server.environment.', + 'server.build.environment.' => 'server.environment.', + ]; + + public function __construct(protected ConnectionInterface $connection) { } @@ -22,13 +34,13 @@ class EggImporterService * * @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable */ - public function fromFile(UploadedFile $file): Egg + public function fromFile(UploadedFile $file, Egg $egg = null): Egg { - $parsed = $this->parser->handle($file); + $parsed = $this->parseFile($file); - return $this->connection->transaction(function () use ($parsed) { + return $this->connection->transaction(function () use ($egg, $parsed) { $uuid = $parsed['uuid'] ?? Uuid::uuid4()->toString(); - $egg = Egg::where('uuid', $uuid)->first() ?? new Egg(); + $egg = $egg ?? Egg::where('uuid', $uuid)->first() ?? new Egg(); $egg = $egg->forceFill([ 'uuid' => $uuid, @@ -36,23 +48,32 @@ class EggImporterService 'copy_script_from' => null, ]); - $egg = $this->parser->fillFromParsed($egg, $parsed); + $egg = $this->fillFromParsed($egg, $parsed); $egg->save(); + // Update existing variables or create new ones. foreach ($parsed['variables'] ?? [] as $variable) { - EggVariable::query()->forceCreate(array_merge($variable, ['egg_id' => $egg->id])); + EggVariable::unguarded(function () use ($egg, $variable) { + $egg->variables()->updateOrCreate([ + 'env_variable' => $variable['env_variable'], + ], Collection::make($variable)->except(['egg_id', 'env_variable'])->toArray()); + }); } - return $egg; + $imported = array_map(fn ($value) => $value['env_variable'], $parsed['variables'] ?? []); + + $egg->variables()->whereNotIn('env_variable', $imported)->delete(); + + return $egg->refresh(); }); } /** - * Take an url and parse it into a new egg. + * Take an url and parse it into a new egg or update an existing one. * * @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable */ - public function fromUrl(string $url): Egg + public function fromUrl(string $url, Egg $egg = null): Egg { $info = pathinfo($url); $tmpDir = TemporaryDirectory::make()->deleteWhenDestroyed(); @@ -60,6 +81,91 @@ class EggImporterService file_put_contents($tmpPath, file_get_contents($url)); - return $this->fromFile(new UploadedFile($tmpPath, $info['basename'], 'application/json')); + return $this->fromFile(new UploadedFile($tmpPath, $info['basename'], 'application/json'), $egg); + } + + /** + * Takes an uploaded file and parses out the egg configuration from within. + * + * @throws \JsonException + * @throws \App\Exceptions\Service\InvalidFileUploadException + */ + protected function parseFile(UploadedFile $file): array + { + if ($file->getError() !== UPLOAD_ERR_OK) { + throw new InvalidFileUploadException('The selected file was not uploaded successfully'); + } + + $parsed = json_decode($file->getContent(), true, 512, JSON_THROW_ON_ERROR); + + $version = $parsed['meta']['version'] ?? ''; + + $parsed = match ($version) { + 'PTDL_v1' => $this->convertToV2($parsed), + 'PTDL_v2' => $parsed, + default => throw new InvalidFileUploadException('The JSON file provided is not in a format that can be recognized.') + }; + + // Make sure we only use recent variable format from now on + $parsed['config']['files'] = str_replace( + array_keys(self::UPGRADE_VARIABLES), + array_values(self::UPGRADE_VARIABLES), + $parsed['config']['files'] ?? '', + ); + + return $parsed; + } + + /** + * Fills the provided model with the parsed JSON data. + */ + protected function fillFromParsed(Egg $model, array $parsed): Egg + { + return $model->forceFill([ + 'name' => Arr::get($parsed, 'name'), + 'description' => Arr::get($parsed, 'description'), + 'features' => Arr::get($parsed, 'features'), + 'docker_images' => Arr::get($parsed, 'docker_images'), + 'file_denylist' => Collection::make(Arr::get($parsed, 'file_denylist')) + ->filter(fn ($value) => !empty($value)), + 'update_url' => Arr::get($parsed, 'meta.update_url'), + 'config_files' => Arr::get($parsed, 'config.files'), + 'config_startup' => Arr::get($parsed, 'config.startup'), + 'config_logs' => Arr::get($parsed, 'config.logs'), + 'config_stop' => Arr::get($parsed, 'config.stop'), + 'startup' => Arr::get($parsed, 'startup'), + 'script_install' => Arr::get($parsed, 'scripts.installation.script'), + 'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'), + 'script_container' => Arr::get($parsed, 'scripts.installation.container'), + ]); + } + + /** + * Converts a PTDL_V1 egg into the expected PTDL_V2 egg format. This just handles + * the "docker_images" field potentially not being present, and not being in the + * expected "key => value" format. + */ + protected function convertToV2(array $parsed): array + { + // Maintain backwards compatability for eggs that are still using the old single image + // string format. New eggs can provide an array of Docker images that can be used. + if (!isset($parsed['images'])) { + $images = [Arr::get($parsed, 'image') ?? 'nil']; + } else { + $images = $parsed['images']; + } + + unset($parsed['images'], $parsed['image']); + + $parsed['docker_images'] = []; + foreach ($images as $image) { + $parsed['docker_images'][$image] = $image; + } + + $parsed['variables'] = array_map(function ($value) { + return array_merge($value, ['field_type' => 'text']); + }, $parsed['variables']); + + return $parsed; } } diff --git a/app/Services/Eggs/Sharing/EggUpdateImporterService.php b/app/Services/Eggs/Sharing/EggUpdateImporterService.php deleted file mode 100644 index a441079e5..000000000 --- a/app/Services/Eggs/Sharing/EggUpdateImporterService.php +++ /dev/null @@ -1,67 +0,0 @@ -parser->handle($file); - - return $this->connection->transaction(function () use ($egg, $parsed) { - $egg = $this->parser->fillFromParsed($egg, $parsed); - $egg->save(); - - // Update existing variables or create new ones. - foreach ($parsed['variables'] ?? [] as $variable) { - EggVariable::unguarded(function () use ($egg, $variable) { - $egg->variables()->updateOrCreate([ - 'env_variable' => $variable['env_variable'], - ], Collection::make($variable)->except(['egg_id', 'env_variable'])->toArray()); - }); - } - - $imported = array_map(fn ($value) => $value['env_variable'], $parsed['variables'] ?? []); - - $egg->variables()->whereNotIn('env_variable', $imported)->delete(); - - return $egg->refresh(); - }); - } - - /** - * Update an existing Egg using an url. - * - * @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable - */ - public function fromUrl(Egg $egg, string $url): Egg - { - $info = pathinfo($url); - $tmpDir = TemporaryDirectory::make()->deleteWhenDestroyed(); - $tmpPath = $tmpDir->path($info['basename']); - - file_put_contents($tmpPath, file_get_contents($url)); - - return $this->fromFile($egg, new UploadedFile($tmpPath, $info['basename'], 'application/json')); - } -} diff --git a/composer.json b/composer.json index 833cef865..481fa145d 100644 --- a/composer.json +++ b/composer.json @@ -88,4 +88,4 @@ }, "minimum-stability": "stable", "prefer-stable": true -} +} \ No newline at end of file diff --git a/database/Seeders/EggSeeder.php b/database/Seeders/EggSeeder.php index afcb55a6d..dcbc4f25c 100644 --- a/database/Seeders/EggSeeder.php +++ b/database/Seeders/EggSeeder.php @@ -7,14 +7,11 @@ use Exception; use Illuminate\Database\Seeder; use Illuminate\Http\UploadedFile; use App\Services\Eggs\Sharing\EggImporterService; -use App\Services\Eggs\Sharing\EggUpdateImporterService; class EggSeeder extends Seeder { protected EggImporterService $importerService; - protected EggUpdateImporterService $updateImporterService; - /** * @var string[] */ @@ -29,11 +26,9 @@ class EggSeeder extends Seeder * EggSeeder constructor. */ public function __construct( - EggImporterService $importerService, - EggUpdateImporterService $updateImporterService + EggImporterService $importerService ) { $this->importerService = $importerService; - $this->updateImporterService = $updateImporterService; } /** @@ -75,7 +70,7 @@ class EggSeeder extends Seeder ->first(); if ($egg instanceof Egg) { - $this->updateImporterService->fromFile($egg, $file); + $this->importerService->fromFile($file, $egg); $this->command->info('Updated ' . $decoded['name']); } else { $this->importerService->fromFile($file); From 7c829fb9cf8e25e145535ea55d801589a68d0857 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Wed, 12 Jun 2024 18:18:24 +0200 Subject: [PATCH 17/27] Fix egg export (#380) --- app/Filament/Resources/EggResource/Pages/EditEgg.php | 6 ++++-- app/Filament/Resources/EggResource/Pages/ListEggs.php | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/Filament/Resources/EggResource/Pages/EditEgg.php b/app/Filament/Resources/EggResource/Pages/EditEgg.php index 8f7fdca98..9a51bdc78 100644 --- a/app/Filament/Resources/EggResource/Pages/EditEgg.php +++ b/app/Filament/Resources/EggResource/Pages/EditEgg.php @@ -202,11 +202,13 @@ class EditEgg extends EditRecord Actions\DeleteAction::make() ->disabled(fn (Egg $egg): bool => $egg->servers()->count() > 0) ->label(fn (Egg $egg): string => $egg->servers()->count() <= 0 ? 'Delete Egg' : 'Egg In Use'), - Actions\ExportAction::make() + Actions\Action::make('export') ->icon('tabler-download') ->label('Export Egg') ->color('primary') - ->url(fn (EggExporterService $service, Egg $egg) => $service->handle($egg->id)), + ->action(fn (EggExporterService $service, Egg $egg) => response()->streamDownload(function () use ($service, $egg) { + echo $service->handle($egg->id); + }, 'egg-' . $egg->getKebabName() . '.json')), $this->getSaveFormAction()->formId('form'), ]; } diff --git a/app/Filament/Resources/EggResource/Pages/ListEggs.php b/app/Filament/Resources/EggResource/Pages/ListEggs.php index 8cd878a71..e51f3bbfc 100644 --- a/app/Filament/Resources/EggResource/Pages/ListEggs.php +++ b/app/Filament/Resources/EggResource/Pages/ListEggs.php @@ -4,6 +4,7 @@ namespace App\Filament\Resources\EggResource\Pages; use App\Filament\Resources\EggResource; use App\Models\Egg; +use App\Services\Eggs\Sharing\EggExporterService; use App\Services\Eggs\Sharing\EggImporterService; use Exception; use Filament\Actions; @@ -43,12 +44,13 @@ class ListEggs extends ListRecords ]) ->actions([ Tables\Actions\EditAction::make(), - Tables\Actions\ExportAction::make() + Tables\Actions\Action::make('export') ->icon('tabler-download') ->label('Export') ->color('primary') - // TODO uses old admin panel export service - ->url(fn (Egg $egg): string => route('admin.eggs.export', ['egg' => $egg])), + ->action(fn (EggExporterService $service, Egg $egg) => response()->streamDownload(function () use ($service, $egg) { + echo $service->handle($egg->id); + }, 'egg-' . $egg->getKebabName() . '.json')), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ From 12518bc5d682a1d401a2c4fd87807615f911e536 Mon Sep 17 00:00:00 2001 From: notCharles Date: Wed, 12 Jun 2024 19:30:02 -0400 Subject: [PATCH 18/27] Allow more searchable Allow more columns to be searchable. --- .../Resources/ServerResource/Pages/ListServers.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/Filament/Resources/ServerResource/Pages/ListServers.php b/app/Filament/Resources/ServerResource/Pages/ListServers.php index 3e20d8830..c60f1c6b0 100644 --- a/app/Filament/Resources/ServerResource/Pages/ListServers.php +++ b/app/Filament/Resources/ServerResource/Pages/ListServers.php @@ -52,16 +52,19 @@ class ListServers extends ListRecords Tables\Columns\TextColumn::make('node.name') ->icon('tabler-server-2') ->url(fn (Server $server): string => route('filament.admin.resources.nodes.edit', ['record' => $server->node])) - ->sortable(), + ->sortable() + ->searchable(), Tables\Columns\TextColumn::make('egg.name') ->icon('tabler-egg') ->url(fn (Server $server): string => route('filament.admin.resources.eggs.edit', ['record' => $server->egg])) - ->sortable(), + ->sortable() + ->searchable(), Tables\Columns\TextColumn::make('user.username') ->icon('tabler-user') ->label('Owner') ->url(fn (Server $server): string => route('filament.admin.resources.users.edit', ['record' => $server->user])) - ->sortable(), + ->sortable() + ->searchable(), Tables\Columns\SelectColumn::make('allocation_id') ->label('Primary Allocation') ->options(fn (Server $server) => $server->allocations->mapWithKeys( @@ -77,9 +80,6 @@ class ListServers extends ListRecords ->numeric() ->sortable(), ]) - ->filters([ - // - ]) ->actions([ Tables\Actions\Action::make('View') ->icon('tabler-terminal') @@ -87,6 +87,7 @@ class ListServers extends ListRecords Tables\Actions\EditAction::make(), ]) ->emptyStateIcon('tabler-brand-docker') + ->searchable() ->emptyStateDescription('') ->emptyStateHeading('No Servers') ->emptyStateActions([ From 0c0b46852526658946629d46ef9a7e786bb619cc Mon Sep 17 00:00:00 2001 From: Boy132 Date: Thu, 13 Jun 2024 08:21:56 +0200 Subject: [PATCH 19/27] Change `allowed_ips` to non-nullable (#373) * change `allowed_ips` to non nullable * fix default value * show "allowed_ips" input --- .../ApiKeyResource/Pages/CreateApiKey.php | 4 +-- app/Models/ApiKey.php | 11 +++++-- database/Factories/ApiKeyFactory.php | 2 +- ...4_make_allowed_ips_column_non_nullable.php | 33 +++++++++++++++++++ 4 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 database/migrations/2024_06_04_133434_make_allowed_ips_column_non_nullable.php diff --git a/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php b/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php index 64fd7c77c..a864660dd 100644 --- a/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php +++ b/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php @@ -71,9 +71,7 @@ class CreateApiKey extends CreateRecord ->placeholder('Example: 127.0.0.1 or 192.168.1.1') ->label('Whitelisted IPv4 Addresses') ->helperText('Press enter to add a new IP address or leave blank to allow any IP address') - ->columnSpanFull() - ->hidden() - ->default(null), + ->columnSpanFull(), Forms\Components\Textarea::make('memo') ->required() diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 21c044c54..52d78a97b 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -15,7 +15,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @property int $key_type * @property string $identifier * @property string $token - * @property array|null $allowed_ips + * @property array $allowed_ips * @property string|null $memo * @property \Illuminate\Support\Carbon|null $last_used_at * @property \Illuminate\Support\Carbon|null $expires_at @@ -113,6 +113,13 @@ class ApiKey extends Model 'r_' . AdminAcl::RESOURCE_MOUNTS, ]; + /** + * Default attributes when creating a new model. + */ + protected $attributes = [ + 'allowed_ips' => '[]', + ]; + /** * Fields that should not be included when calling toArray() or toJson() * on this model. @@ -128,7 +135,7 @@ class ApiKey extends Model 'identifier' => 'required|string|size:16|unique:api_keys,identifier', 'token' => 'required|string', 'memo' => 'required|nullable|string|max:500', - 'allowed_ips' => 'nullable|array', + 'allowed_ips' => 'array', 'allowed_ips.*' => 'string', 'last_used_at' => 'nullable|date', 'expires_at' => 'nullable|date', diff --git a/database/Factories/ApiKeyFactory.php b/database/Factories/ApiKeyFactory.php index 3361433b4..1b258767b 100644 --- a/database/Factories/ApiKeyFactory.php +++ b/database/Factories/ApiKeyFactory.php @@ -27,7 +27,7 @@ class ApiKeyFactory extends Factory 'key_type' => ApiKey::TYPE_APPLICATION, 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION), 'token' => $token ?: $token = Str::random(ApiKey::KEY_LENGTH), - 'allowed_ips' => null, + 'allowed_ips' => [], 'memo' => 'Test Function Key', 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), diff --git a/database/migrations/2024_06_04_133434_make_allowed_ips_column_non_nullable.php b/database/migrations/2024_06_04_133434_make_allowed_ips_column_non_nullable.php new file mode 100644 index 000000000..907981734 --- /dev/null +++ b/database/migrations/2024_06_04_133434_make_allowed_ips_column_non_nullable.php @@ -0,0 +1,33 @@ +whereNull('allowed_ips')->update([ + 'allowed_ips' => '[]', + ]); + + Schema::table('api_keys', function (Blueprint $table) { + $table->text('allowed_ips')->nullable(false)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('api_keys', function (Blueprint $table) { + $table->text('allowed_ips')->nullable()->change(); + }); + } +}; From cd4fc1a95d536b9638536f1d16158e59e8224f1e Mon Sep 17 00:00:00 2001 From: Boy132 Date: Thu, 13 Jun 2024 08:22:29 +0200 Subject: [PATCH 20/27] Add config variable to change between `MiB` and `MB` (#374) --- app/Filament/Resources/NodeResource/Pages/CreateNode.php | 6 +++--- app/Filament/Resources/NodeResource/Pages/EditNode.php | 6 +++--- app/Filament/Resources/NodeResource/Pages/ListNodes.php | 8 ++++---- .../Resources/ServerResource/Pages/CreateServer.php | 6 +++--- .../Resources/ServerResource/Pages/EditServer.php | 6 +++--- .../Servers/ServerConfigurationStructureService.php | 6 +++--- config/panel.php | 2 ++ 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/app/Filament/Resources/NodeResource/Pages/CreateNode.php b/app/Filament/Resources/NodeResource/Pages/CreateNode.php index bd21dc558..0dbf4c2b4 100644 --- a/app/Filament/Resources/NodeResource/Pages/CreateNode.php +++ b/app/Filament/Resources/NodeResource/Pages/CreateNode.php @@ -239,7 +239,7 @@ class CreateNode extends CreateRecord ->default(256) ->minValue(1) ->maxValue(1024) - ->suffix('MiB'), + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB'), Forms\Components\TextInput::make('daemon_sftp') ->columnSpan(1) ->label('SFTP Port') @@ -275,7 +275,7 @@ class CreateNode extends CreateRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => $get('unlimited_mem')) ->label('Memory Limit')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->columnSpan(2) ->numeric() ->minValue(0) @@ -316,7 +316,7 @@ class CreateNode extends CreateRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => $get('unlimited_disk')) ->label('Disk Limit')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->columnSpan(2) ->numeric() ->minValue(0) diff --git a/app/Filament/Resources/NodeResource/Pages/EditNode.php b/app/Filament/Resources/NodeResource/Pages/EditNode.php index 4b7f90ad1..dded9ffd2 100644 --- a/app/Filament/Resources/NodeResource/Pages/EditNode.php +++ b/app/Filament/Resources/NodeResource/Pages/EditNode.php @@ -214,7 +214,7 @@ class EditNode extends EditRecord ->numeric()->required() ->minValue(1) ->maxValue(1024) - ->suffix('MiB'), + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB'), Forms\Components\TextInput::make('daemon_sftp') ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3]) ->label('SFTP Port') @@ -274,7 +274,7 @@ class EditNode extends EditRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => $get('unlimited_mem')) ->label('Memory Limit')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->required() ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2]) ->numeric() @@ -314,7 +314,7 @@ class EditNode extends EditRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => $get('unlimited_disk')) ->label('Disk Limit')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->required() ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2]) ->numeric() diff --git a/app/Filament/Resources/NodeResource/Pages/ListNodes.php b/app/Filament/Resources/NodeResource/Pages/ListNodes.php index 1496e7aee..81dd52fb0 100644 --- a/app/Filament/Resources/NodeResource/Pages/ListNodes.php +++ b/app/Filament/Resources/NodeResource/Pages/ListNodes.php @@ -42,15 +42,15 @@ class ListNodes extends ListRecords ->visibleFrom('sm') ->icon('tabler-device-desktop-analytics') ->numeric() - ->suffix(' GiB') - ->formatStateUsing(fn ($state) => number_format($state / 1024, 2)) + ->suffix(config('panel.use_binary_prefix') ? ' GiB' : ' GB') + ->formatStateUsing(fn ($state) => number_format($state / (config('panel.use_binary_prefix') ? 1024 : 1000), 2)) ->sortable(), Tables\Columns\TextColumn::make('disk') ->visibleFrom('sm') ->icon('tabler-file') ->numeric() - ->suffix(' GiB') - ->formatStateUsing(fn ($state) => number_format($state / 1024, 2)) + ->suffix(config('panel.use_binary_prefix') ? ' GiB' : ' GB') + ->formatStateUsing(fn ($state) => number_format($state / (config('panel.use_binary_prefix') ? 1024 : 1000), 2)) ->sortable(), Tables\Columns\TextColumn::make('cpu') ->visibleFrom('sm') diff --git a/app/Filament/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Resources/ServerResource/Pages/CreateServer.php index d61c141fe..9d55bcaed 100644 --- a/app/Filament/Resources/ServerResource/Pages/CreateServer.php +++ b/app/Filament/Resources/ServerResource/Pages/CreateServer.php @@ -488,7 +488,7 @@ class CreateServer extends CreateRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => $get('unlimited_mem')) ->label('Memory Limit')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->default(0) ->required() ->columnSpan(2) @@ -519,7 +519,7 @@ class CreateServer extends CreateRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => $get('unlimited_disk')) ->label('Disk Space Limit')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->default(0) ->required() ->columnSpan(2) @@ -599,7 +599,7 @@ class CreateServer extends CreateRecord }) ->label('Swap Memory') ->default(0) - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->minValue(-1) ->columnSpan(2) ->inlineLabel() diff --git a/app/Filament/Resources/ServerResource/Pages/EditServer.php b/app/Filament/Resources/ServerResource/Pages/EditServer.php index c7a3ae4e5..c51a9a569 100644 --- a/app/Filament/Resources/ServerResource/Pages/EditServer.php +++ b/app/Filament/Resources/ServerResource/Pages/EditServer.php @@ -220,7 +220,7 @@ class EditServer extends EditRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => $get('unlimited_mem')) ->label('Memory Limit')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->required() ->columnSpan(2) ->numeric() @@ -250,7 +250,7 @@ class EditServer extends EditRecord ->dehydratedWhenHidden() ->hidden(fn (Forms\Get $get) => $get('unlimited_disk')) ->label('Disk Space Limit')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->required() ->columnSpan(2) ->numeric() @@ -331,7 +331,7 @@ class EditServer extends EditRecord default => false, }) ->label('Swap Memory')->inlineLabel() - ->suffix('MiB') + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->minValue(-1) ->columnSpan(2) ->required() diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php index 43fd9f76c..d9d0f7fe6 100644 --- a/app/Services/Servers/ServerConfigurationStructureService.php +++ b/app/Services/Servers/ServerConfigurationStructureService.php @@ -51,12 +51,12 @@ class ServerConfigurationStructureService 'invocation' => $server->startup, 'skip_egg_scripts' => $server->skip_scripts, 'build' => [ - 'memory_limit' => $server->memory, - 'swap' => $server->swap, + 'memory_limit' => config('panel.use_binary_prefix') ? $server->memory : $server->memory / 1.048576, + 'swap' => config('panel.use_binary_prefix') ? $server->swap : $server->swap / 1.048576, 'io_weight' => $server->io, 'cpu_limit' => $server->cpu, 'threads' => $server->threads, - 'disk_space' => $server->disk, + 'disk_space' => config('panel.use_binary_prefix') ? $server->disk : $server->disk / 1.048576, 'oom_killer' => $server->oom_killer, ], 'container' => [ diff --git a/config/panel.php b/config/panel.php index ea408008e..7b049f145 100644 --- a/config/panel.php +++ b/config/panel.php @@ -175,4 +175,6 @@ return [ 'filament' => [ 'top-navigation' => env('FILAMENT_TOP_NAVIGATION', false), ], + + 'use_binary_prefix' => env('PANEL_USE_BINARY_PREFIX', true), ]; From ce1163d3875f2e89b216aa915c13c90b659a0304 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Thu, 13 Jun 2024 08:23:24 +0200 Subject: [PATCH 21/27] Hide task delay on frontend if queue driver is set to `sync` (#375) --- app/Http/ViewComposers/AssetComposer.php | 1 + .../server/schedules/ScheduleTaskRow.tsx | 4 +++- .../server/schedules/TaskDetailsModal.tsx | 24 +++++++++++-------- .../server/settings/SettingsContainer.tsx | 5 ++-- resources/scripts/state/settings.ts | 1 + 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/app/Http/ViewComposers/AssetComposer.php b/app/Http/ViewComposers/AssetComposer.php index da6798ca7..2a47096fb 100644 --- a/app/Http/ViewComposers/AssetComposer.php +++ b/app/Http/ViewComposers/AssetComposer.php @@ -27,6 +27,7 @@ class AssetComposer 'enabled' => config('recaptcha.enabled', false), 'siteKey' => config('recaptcha.website_key') ?? '', ], + 'usesSyncDriver' => config('queue.default') === 'sync', ]); } } diff --git a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx index f950b94f8..36db1fff9 100644 --- a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx +++ b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx @@ -20,6 +20,7 @@ import { ServerContext } from '@/state/server'; import tw from 'twin.macro'; import ConfirmationModal from '@/components/elements/ConfirmationModal'; import Icon from '@/components/elements/Icon'; +import { useStoreState } from 'easy-peasy'; interface Props { schedule: Schedule; @@ -46,6 +47,7 @@ export default ({ schedule, task }: Props) => { const [isLoading, setIsLoading] = useState(false); const [isEditing, setIsEditing] = useState(false); const appendSchedule = ServerContext.useStoreActions((actions) => actions.schedules.appendSchedule); + const usesSyncDriver = useStoreState((state) => state.settings.data!.usesSyncDriver); const onConfirmDeletion = () => { setIsLoading(true); @@ -109,7 +111,7 @@ export default ({ schedule, task }: Props) => { )} - {task.sequenceId > 1 && task.timeOffset > 0 && ( + {!usesSyncDriver && task.sequenceId > 1 && task.timeOffset > 0 && (
diff --git a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx index 6dcd9e932..0af8fb441 100644 --- a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx +++ b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx @@ -17,6 +17,7 @@ import Select from '@/components/elements/Select'; import ModalContext from '@/context/ModalContext'; import asModal from '@/hoc/asModal'; import FormikSwitch from '@/components/elements/FormikSwitch'; +import { useStoreState } from 'easy-peasy'; interface Props { schedule: Schedule; @@ -71,6 +72,7 @@ const TaskDetailsModal = ({ schedule, task }: Props) => { const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid); const appendSchedule = ServerContext.useStoreActions((actions) => actions.schedules.appendSchedule); const backupLimit = ServerContext.useStoreState((state) => state.server.data!.featureLimits.backups); + const usesSyncDriver = useStoreState((state) => state.settings.data!.usesSyncDriver); useEffect(() => { return () => { @@ -121,7 +123,7 @@ const TaskDetailsModal = ({ schedule, task }: Props) => {

{task ? 'Edit Task' : 'Create Task'}

-
+
@@ -132,15 +134,17 @@ const TaskDetailsModal = ({ schedule, task }: Props) => {
-
- -
+ {!usesSyncDriver && ( +
+ +
+ )}
{values.action === 'command' ? ( diff --git a/resources/scripts/components/server/settings/SettingsContainer.tsx b/resources/scripts/components/server/settings/SettingsContainer.tsx index 253972755..a69246427 100644 --- a/resources/scripts/components/server/settings/SettingsContainer.tsx +++ b/resources/scripts/components/server/settings/SettingsContainer.tsx @@ -55,8 +55,9 @@ export default () => {
Launch SFTP diff --git a/resources/scripts/state/settings.ts b/resources/scripts/state/settings.ts index 20dbbdc6e..06f82a02e 100644 --- a/resources/scripts/state/settings.ts +++ b/resources/scripts/state/settings.ts @@ -7,6 +7,7 @@ export interface SiteSettings { enabled: boolean; siteKey: string; }; + usesSyncDriver: boolean; } export interface SettingsStore { From 0da184c56ea8995c7a62adb42de30a1f6e71d4b4 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Thu, 13 Jun 2024 08:23:45 +0200 Subject: [PATCH 22/27] Get latest Panel & Wings version from github api (#379) * get latest panel % wings version from github api * fix tests * remove unused CdnVersionFetchingException --- .../Helper/CdnVersionFetchingException.php | 7 ------ .../Helpers/SoftwareVersionService.php | 25 +++++++++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) delete mode 100644 app/Exceptions/Service/Helper/CdnVersionFetchingException.php diff --git a/app/Exceptions/Service/Helper/CdnVersionFetchingException.php b/app/Exceptions/Service/Helper/CdnVersionFetchingException.php deleted file mode 100644 index 3a2575f58..000000000 --- a/app/Exceptions/Service/Helper/CdnVersionFetchingException.php +++ /dev/null @@ -1,7 +0,0 @@ -cache->remember(self::VERSION_CACHE_KEY, CarbonImmutable::now()->addMinutes(config('panel.cdn.cache_time', 60)), function () { - try { - $response = $this->client->request('GET', config('panel.cdn.url')); + $versionData = []; + try { + $response = $this->client->request('GET', 'https://api.github.com/repos/pelican-dev/panel/releases/latest'); if ($response->getStatusCode() === 200) { - return json_decode($response->getBody(), true); + $panelData = json_decode($response->getBody(), true); + $versionData['panel'] = trim($panelData['tag_name'], 'v'); } - throw new CdnVersionFetchingException(); - } catch (Exception) { - return []; + $response = $this->client->request('GET', 'https://api.github.com/repos/pelican-dev/wings/releases/latest'); + if ($response->getStatusCode() === 200) { + $wingsData = json_decode($response->getBody(), true); + $versionData['daemon'] = trim($wingsData['tag_name'], 'v'); + } + } catch (ClientException $e) { } + + $versionData['discord'] = 'https://pelican.dev/discord'; + $versionData['donate'] = 'https://pelican.dev/donate'; + + return $versionData; }); } From 6916b89638d31ab7c27e3a9695f9c48e7e1778ba Mon Sep 17 00:00:00 2001 From: Boy132 Date: Thu, 13 Jun 2024 15:48:36 +0200 Subject: [PATCH 23/27] Fill startup & image from egg if missing in request (#384) --- .../Api/Application/Servers/StoreServerRequest.php | 4 ++-- .../Application/Servers/UpdateServerStartupRequest.php | 4 ++-- app/Services/Servers/ServerCreationService.php | 8 ++++++++ app/Services/Servers/StartupModificationService.php | 4 ++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php index 263b7c540..86ac941fd 100644 --- a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php @@ -28,8 +28,8 @@ class StoreServerRequest extends ApplicationApiRequest 'description' => array_merge(['nullable'], $rules['description']), 'user' => $rules['owner_id'], 'egg' => $rules['egg_id'], - 'docker_image' => $rules['image'], - 'startup' => $rules['startup'], + 'docker_image' => 'sometimes|string', + 'startup' => 'sometimes|string', 'environment' => 'present|array', 'skip_scripts' => 'sometimes|boolean', 'oom_killer' => 'sometimes|boolean', diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php index 11e15977d..47b74f886 100644 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php @@ -20,10 +20,10 @@ class UpdateServerStartupRequest extends ApplicationApiRequest $data = Server::getRulesForUpdate($this->parameter('server', Server::class)); return [ - 'startup' => $data['startup'], + 'startup' => 'sometimes|string', 'environment' => 'present|array', 'egg' => $data['egg_id'], - 'image' => $data['image'], + 'image' => 'sometimes|string', 'skip_scripts' => 'present|boolean', ]; } diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php index d555f7876..7fd3e8b67 100644 --- a/app/Services/Servers/ServerCreationService.php +++ b/app/Services/Servers/ServerCreationService.php @@ -17,6 +17,7 @@ use App\Repositories\Daemon\DaemonServerRepository; use App\Services\Deployment\FindViableNodesService; use App\Services\Deployment\AllocationSelectionService; use App\Exceptions\Http\Connection\DaemonConnectionException; +use App\Models\Egg; class ServerCreationService { @@ -50,6 +51,13 @@ class ServerCreationService $data['oom_killer'] = !$data['oom_disabled']; } + /** @var Egg $egg */ + $egg = Egg::query()->findOrFail($data['egg_id']); + + // Fill missing fields from egg + $data['image'] = $data['image'] ?? collect($egg->docker_images)->first(); + $data['startup'] = $data['startup'] ?? $egg->startup; + // If a deployment object has been passed we need to get the allocation // that the server should use, and assign the node from that allocation. if ($deployment instanceof DeploymentObject) { diff --git a/app/Services/Servers/StartupModificationService.php b/app/Services/Servers/StartupModificationService.php index 25f14d39d..c8eb95cf6 100644 --- a/app/Services/Servers/StartupModificationService.php +++ b/app/Services/Servers/StartupModificationService.php @@ -76,6 +76,10 @@ class StartupModificationService $server = $server->forceFill([ 'egg_id' => $egg->id, ]); + + // Fill missing fields from egg + $data['docker_image'] = $data['docker_image'] ?? collect($egg->docker_images)->first(); + $data['startup'] = $data['startup'] ?? $egg->startup; } $server->fill([ From 5a3c606627d7cce919147f9c054d3a3e26c0b572 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Thu, 13 Jun 2024 21:06:31 +0200 Subject: [PATCH 24/27] Add OAuth backend (#386) * add socialite backend * fix redirect url * small cleanup * fix "oauth" type * changes from review --- app/Http/Controllers/Auth/OAuthController.php | 61 ++++ app/Http/Controllers/Base/OauthController.php | 44 +++ app/Models/User.php | 7 +- app/Providers/AppServiceProvider.php | 5 + bootstrap/providers.php | 2 + composer.json | 2 + composer.lock | 339 +++++++++++++++++- config/services.php | 12 + database/Factories/UserFactory.php | 1 + ...06_13_120409_add_oauth_column_to_users.php | 28 ++ routes/auth.php | 4 + routes/base.php | 3 + 12 files changed, 505 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/Auth/OAuthController.php create mode 100644 app/Http/Controllers/Base/OauthController.php create mode 100644 database/migrations/2024_06_13_120409_add_oauth_column_to_users.php diff --git a/app/Http/Controllers/Auth/OAuthController.php b/app/Http/Controllers/Auth/OAuthController.php new file mode 100644 index 000000000..ed24b0f23 --- /dev/null +++ b/app/Http/Controllers/Auth/OAuthController.php @@ -0,0 +1,61 @@ +redirect(); + } + + /** + * Callback from OAuth provider. + */ + protected function callback(Request $request, string $driver): RedirectResponse + { + $oauthUser = Socialite::driver($driver)->user(); + + // User is already logged in and wants to link a new OAuth Provider + if ($request->user()) { + $oauth = $request->user()->oauth; + $oauth[$driver] = $oauthUser->getId(); + + $this->updateService->handle($request->user(), ['oauth' => json_encode($oauth)]); + + return redirect()->route('account'); + } + + try { + $user = User::query()->whereJsonContains('oauth->'. $driver, $oauthUser->getId())->firstOrFail(); + + $this->auth->guard()->login($user, true); + } catch (Exception $e) { + // No user found - redirect to normal login + return redirect()->route('auth.login'); + } + + return redirect('/'); + } +} diff --git a/app/Http/Controllers/Base/OauthController.php b/app/Http/Controllers/Base/OauthController.php new file mode 100644 index 000000000..8b2632b6e --- /dev/null +++ b/app/Http/Controllers/Base/OauthController.php @@ -0,0 +1,44 @@ +get('driver'); + + return Socialite::with($driver)->redirect(); + } + + /** + * Remove a OAuth link + */ + protected function unlink(Request $request): Response + { + $oauth = $request->user()->oauth; + unset($oauth[$request->get('driver')]); + + $this->updateService->handle($request->user(), ['oauth' => json_encode($oauth)]); + + return new Response('', Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index f089aba07..ef9084d82 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -42,6 +42,7 @@ use App\Notifications\SendPasswordReset as ResetPasswordNotification; * @property bool $use_totp * @property string|null $totp_secret * @property \Illuminate\Support\Carbon|null $totp_authenticated_at + * @property array $oauth * @property bool $gravatar * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at @@ -127,12 +128,13 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac 'totp_authenticated_at', 'gravatar', 'root_admin', + 'oauth', ]; /** * The attributes excluded from the model's JSON form. */ - protected $hidden = ['password', 'remember_token', 'totp_secret', 'totp_authenticated_at']; + protected $hidden = ['password', 'remember_token', 'totp_secret', 'totp_authenticated_at', 'oauth']; /** * Default values for specific fields in the database. @@ -145,6 +147,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac 'totp_secret' => null, 'name_first' => '', 'name_last' => '', + 'oauth' => '[]', ]; /** @@ -162,6 +165,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac 'language' => 'string', 'use_totp' => 'boolean', 'totp_secret' => 'nullable|string', + 'oauth' => 'array', ]; protected function casts(): array @@ -172,6 +176,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac 'gravatar' => 'boolean', 'totp_authenticated_at' => 'datetime', 'totp_secret' => 'encrypted', + 'oauth' => 'array', ]; } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 5817890b1..c2473e87d 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -13,6 +13,7 @@ use Dedoc\Scramble\Support\Generator\SecurityScheme; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Pagination\Paginator; use Illuminate\Support\Facades\Broadcast; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Schema; @@ -78,6 +79,10 @@ class AppServiceProvider extends ServiceProvider Scramble::registerApi('application', ['api_path' => 'api/application', 'info' => ['version' => '1.0']]); Scramble::registerApi('client', ['api_path' => 'api/client', 'info' => ['version' => '1.0']])->afterOpenApiGenerated($bearerTokens); Scramble::registerApi('remote', ['api_path' => 'api/remote', 'info' => ['version' => '1.0']])->afterOpenApiGenerated($bearerTokens); + + Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) { + $event->extendSocialite('discord', \SocialiteProviders\Discord\Provider::class); + }); } /** diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 80be25c8e..8c37bb7b5 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -8,4 +8,6 @@ return [ App\Providers\Filament\AdminPanelProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\ViewComposerServiceProvider::class, + + SocialiteProviders\Manager\ServiceProvider::class, ]; diff --git a/composer.json b/composer.json index 481fa145d..aaa3e9bc5 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "laravel/framework": "^11.7", "laravel/helpers": "^1.7", "laravel/sanctum": "^4.0.2", + "laravel/socialite": "^5.14", "laravel/tinker": "^2.9", "laravel/ui": "^4.5.1", "lcobucci/jwt": "~4.3.0", @@ -31,6 +32,7 @@ "prologue/alerts": "^1.2", "ryangjchandler/blade-tabler-icons": "^2.3", "s1lentium/iptools": "~1.2.0", + "socialiteproviders/discord": "^4.2", "spatie/laravel-fractal": "^6.2", "spatie/laravel-query-builder": "^5.8.1", "spatie/temporary-directory": "^2.2", diff --git a/composer.lock b/composer.lock index ba784fff9..22543ba07 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bf44faee3aae2b1d4c1b57893c1aba98", + "content-hash": "443ec1d95b892b261af5481f27b31083", "packages": [ { "name": "abdelhamiderrahmouni/filament-monaco-editor", @@ -2069,6 +2069,69 @@ }, "time": "2024-06-05T09:38:52+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v6.10.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "500501c2ce893c824c801da135d02661199f60c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5", + "reference": "500501c2ce893c824c801da135d02661199f60c5", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.10.1" + }, + "time": "2024-05-18T18:05:11+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.3.0", @@ -3180,6 +3243,78 @@ }, "time": "2023-11-08T14:08:06+00:00" }, + { + "name": "laravel/socialite", + "version": "v5.14.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "c7b0193a3753a29aff8ce80aa2f511917e6ed68a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/c7b0193a3753a29aff8ce80aa2f511917e6ed68a", + "reference": "c7b0193a3753a29aff8ce80aa2f511917e6ed68a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "firebase/php-jwt": "^6.4", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "league/oauth1-client": "^1.10.1", + "php": "^7.2|^8.0", + "phpseclib/phpseclib": "^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.0|^9.3|^10.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ], + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + } + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2024-05-03T20:31:38+00:00" + }, { "name": "laravel/tinker", "version": "v2.9.0", @@ -4114,6 +4249,82 @@ ], "time": "2024-01-28T23:22:08+00:00" }, + { + "name": "league/oauth1-client", + "version": "v1.10.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "d6365b901b5c287dd41f143033315e2f777e1167" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167", + "reference": "d6365b901b5c287dd41f143033315e2f777e1167", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.1" + }, + "time": "2022-04-15T14:02:14+00:00" + }, { "name": "league/uri", "version": "7.4.1", @@ -6579,6 +6790,130 @@ }, "time": "2022-08-17T14:28:59+00:00" }, + { + "name": "socialiteproviders/discord", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Discord.git", + "reference": "c71c379acfdca5ba4aa65a3db5ae5222852a919c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Discord/zipball/c71c379acfdca5ba4aa65a3db5ae5222852a919c", + "reference": "c71c379acfdca5ba4aa65a3db5ae5222852a919c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0", + "socialiteproviders/manager": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Discord\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christopher Eklund", + "email": "eklundchristopher@gmail.com" + } + ], + "description": "Discord OAuth2 Provider for Laravel Socialite", + "keywords": [ + "discord", + "laravel", + "oauth", + "provider", + "socialite" + ], + "support": { + "docs": "https://socialiteproviders.com/discord", + "issues": "https://github.com/socialiteproviders/providers/issues", + "source": "https://github.com/socialiteproviders/providers" + }, + "time": "2023-07-24T23:28:47+00:00" + }, + { + "name": "socialiteproviders/manager", + "version": "v4.6.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Manager.git", + "reference": "dea5190981c31b89e52259da9ab1ca4e2b258b21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/dea5190981c31b89e52259da9ab1ca4e2b258b21", + "reference": "dea5190981c31b89e52259da9ab1ca4e2b258b21", + "shasum": "" + }, + "require": { + "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0", + "laravel/socialite": "^5.5", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "SocialiteProviders\\Manager\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "SocialiteProviders\\Manager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andy Wendt", + "email": "andy@awendt.com" + }, + { + "name": "Anton Komarev", + "email": "a.komarev@cybercog.su" + }, + { + "name": "Miguel Piedrafita", + "email": "soy@miguelpiedrafita.com" + }, + { + "name": "atymic", + "email": "atymicq@gmail.com", + "homepage": "https://atymic.dev" + } + ], + "description": "Easily add new or override built-in providers in Laravel Socialite.", + "homepage": "https://socialiteproviders.com", + "keywords": [ + "laravel", + "manager", + "oauth", + "providers", + "socialite" + ], + "support": { + "issues": "https://github.com/socialiteproviders/manager/issues", + "source": "https://github.com/socialiteproviders/manager" + }, + "time": "2024-05-04T07:57:39+00:00" + }, { "name": "spatie/color", "version": "1.5.3", @@ -13141,5 +13476,5 @@ "ext-zip": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/config/services.php b/config/services.php index 62e0a08a8..7311fdc16 100644 --- a/config/services.php +++ b/config/services.php @@ -9,4 +9,16 @@ return [ 'scheme' => 'https', ], + 'github' => [ + 'client_id' => env('OAUTH_GITHUB_CLIENT_ID'), + 'client_secret' => env('OAUTH_GITHUB_CLIENT_SECRET'), + 'redirect' => '/auth/oauth/callback/github', + ], + + 'discord' => [ + 'client_id' => env('OAUTH_DISCORD_CLIENT_ID'), + 'client_secret' => env('OAUTH_DISCORD_CLIENT_SECRET'), + 'redirect' => '/auth/oauth/callback/discord', + ], + ]; diff --git a/database/Factories/UserFactory.php b/database/Factories/UserFactory.php index 74d2cdbb4..510edaef1 100644 --- a/database/Factories/UserFactory.php +++ b/database/Factories/UserFactory.php @@ -35,6 +35,7 @@ class UserFactory extends Factory 'language' => 'en', 'root_admin' => false, 'use_totp' => false, + 'oauth' => [], 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]; diff --git a/database/migrations/2024_06_13_120409_add_oauth_column_to_users.php b/database/migrations/2024_06_13_120409_add_oauth_column_to_users.php new file mode 100644 index 000000000..22273db94 --- /dev/null +++ b/database/migrations/2024_06_13_120409_add_oauth_column_to_users.php @@ -0,0 +1,28 @@ +json('oauth')->after('totp_authenticated_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('oauth'); + }); + } +}; diff --git a/routes/auth.php b/routes/auth.php index 517afb742..7c572c91b 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -18,6 +18,10 @@ Route::get('/login', [Auth\LoginController::class, 'index'])->name('auth.login') Route::get('/password', [Auth\LoginController::class, 'index'])->name('auth.forgot-password'); Route::get('/password/reset/{token}', [Auth\LoginController::class, 'index'])->name('auth.reset'); +// Endpoints for OAuth +Route::get('/oauth/redirect/{driver}', [Auth\OAuthController::class, 'redirect'])->name('auth.oauth.redirect'); +Route::get('/oauth/callback/{driver}', [Auth\OAuthController::class, 'callback'])->name('auth.oauth.callback'); + // Apply a throttle to authentication action endpoints, in addition to the // recaptcha endpoints to slow down manual attack spammers even more. 🤷‍ // diff --git a/routes/base.php b/routes/base.php index d1dbf7fb4..6fbc41ae8 100644 --- a/routes/base.php +++ b/routes/base.php @@ -9,6 +9,9 @@ Route::get('/account', [Base\IndexController::class, 'index']) ->withoutMiddleware(RequireTwoFactorAuthentication::class) ->name('account'); +Route::get('/account/oauth/link', [Base\OAuthController::class, 'link'])->name('account.oauth.link'); +Route::get('/account/oauth/unlink', [Base\OAuthController::class, 'unlink'])->name('account.oauth.unlink'); + Route::get('/locales/locale.json', Base\LocaleController::class) ->withoutMiddleware(['auth', RequireTwoFactorAuthentication::class]) ->where('namespace', '.*'); From 4f43e9171a7b331314f9e1b51ff135d75b8ada0b Mon Sep 17 00:00:00 2001 From: MartinOscar <40749467+RMartinOscar@users.noreply.github.com> Date: Fri, 14 Jun 2024 05:52:53 +0200 Subject: [PATCH 25/27] Rename OauthController.php to OAuthController.php (#388) Fixes Class App\Http\Controllers\Base\OAuthController located in ./app/Http/Controllers/Base/OauthController.php does not comply with psr-4 autoloading standard (rule: App\ => ./app). Skipping. --- .../Controllers/Base/{OauthController.php => OAuthController.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/Http/Controllers/Base/{OauthController.php => OAuthController.php} (100%) diff --git a/app/Http/Controllers/Base/OauthController.php b/app/Http/Controllers/Base/OAuthController.php similarity index 100% rename from app/Http/Controllers/Base/OauthController.php rename to app/Http/Controllers/Base/OAuthController.php From 7a6edab79a47ccb8bafedbe4f3c4634559e55b35 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Fri, 14 Jun 2024 17:17:34 +0200 Subject: [PATCH 26/27] Remove unnecessary `json_encode` in oauthcontrollers (#391) --- app/Http/Controllers/Auth/OAuthController.php | 2 +- app/Http/Controllers/Base/OAuthController.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Auth/OAuthController.php b/app/Http/Controllers/Auth/OAuthController.php index ed24b0f23..2989761ef 100644 --- a/app/Http/Controllers/Auth/OAuthController.php +++ b/app/Http/Controllers/Auth/OAuthController.php @@ -42,7 +42,7 @@ class OAuthController extends Controller $oauth = $request->user()->oauth; $oauth[$driver] = $oauthUser->getId(); - $this->updateService->handle($request->user(), ['oauth' => json_encode($oauth)]); + $this->updateService->handle($request->user(), ['oauth' => $oauth]); return redirect()->route('account'); } diff --git a/app/Http/Controllers/Base/OAuthController.php b/app/Http/Controllers/Base/OAuthController.php index 8b2632b6e..bb078a3c8 100644 --- a/app/Http/Controllers/Base/OAuthController.php +++ b/app/Http/Controllers/Base/OAuthController.php @@ -37,7 +37,7 @@ class OAuthController extends Controller $oauth = $request->user()->oauth; unset($oauth[$request->get('driver')]); - $this->updateService->handle($request->user(), ['oauth' => json_encode($oauth)]); + $this->updateService->handle($request->user(), ['oauth' => $oauth]); return new Response('', Response::HTTP_NO_CONTENT); } From 42ecd2951d34f53d164b90b520b99abee21ad211 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Fri, 14 Jun 2024 17:17:49 +0200 Subject: [PATCH 27/27] Update `p:info` command (#389) --- app/Console/Commands/InfoCommand.php | 79 +++++++++++++++++++--------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/app/Console/Commands/InfoCommand.php b/app/Console/Commands/InfoCommand.php index 53b1dd889..c093259e0 100644 --- a/app/Console/Commands/InfoCommand.php +++ b/app/Console/Commands/InfoCommand.php @@ -7,12 +7,12 @@ use App\Services\Helpers\SoftwareVersionService; class InfoCommand extends Command { - protected $description = 'Displays the application, database, and email configurations along with the panel version.'; + protected $description = 'Displays the application, database, email and backup configurations along with the panel version.'; protected $signature = 'p:info'; /** - * VersionCommand constructor. + * InfoCommand constructor. */ public function __construct(private SoftwareVersionService $versionService) { @@ -33,38 +33,69 @@ class InfoCommand extends Command $this->output->title('Application Configuration'); $this->table([], [ - ['Environment', $this->formatText(config('app.env'), config('app.env') === 'production' ?: 'bg=red')], - ['Debug Mode', $this->formatText(config('app.debug') ? 'Yes' : 'No', !config('app.debug') ?: 'bg=red')], - ['Installation URL', config('app.url')], + ['Environment', config('app.env') === 'production' ? config('app.env') : $this->formatText(config('app.env'), 'bg=red')], + ['Debug Mode', config('app.debug') ? $this->formatText('Yes', 'bg=red') : 'No'], + ['Application Name', config('app.name')], + ['Application URL', config('app.url')], ['Installation Directory', base_path()], ['Cache Driver', config('cache.default')], - ['Queue Driver', config('queue.default')], + ['Queue Driver', config('queue.default') === 'sync' ? $this->formatText(config('queue.default'), 'bg=red') : config('queue.default')], ['Session Driver', config('session.driver')], ['Filesystem Driver', config('filesystems.default')], - ['Default Theme', config('themes.active')], ], 'compact'); $this->output->title('Database Configuration'); $driver = config('database.default'); - $this->table([], [ - ['Driver', $driver], - ['Host', config("database.connections.$driver.host")], - ['Port', config("database.connections.$driver.port")], - ['Database', config("database.connections.$driver.database")], - ['Username', config("database.connections.$driver.username")], - ], 'compact'); + if ($driver === 'sqlite') { + $this->table([], [ + ['Driver', $driver], + ['Database', config("database.connections.$driver.database")], + ], 'compact'); + } else { + $this->table([], [ + ['Driver', $driver], + ['Host', config("database.connections.$driver.host")], + ['Port', config("database.connections.$driver.port")], + ['Database', config("database.connections.$driver.database")], + ['Username', config("database.connections.$driver.username")], + ], 'compact'); + } - // TODO: Update this to handle other mail drivers $this->output->title('Email Configuration'); - $this->table([], [ - ['Driver', config('mail.default')], - ['Host', config('mail.mailers.smtp.host')], - ['Port', config('mail.mailers.smtp.port')], - ['Username', config('mail.mailers.smtp.username')], - ['From Address', config('mail.from.address')], - ['From Name', config('mail.from.name')], - ['Encryption', config('mail.mailers.smtp.encryption')], - ], 'compact'); + $driver = config('mail.default'); + if ($driver === 'smtp') { + $this->table([], [ + ['Driver', $driver], + ['Host', config("mail.mailers.$driver.host")], + ['Port', config("mail.mailers.$driver.port")], + ['Username', config("mail.mailers.$driver.username")], + ['Encryption', config("mail.mailers.$driver.encryption")], + ['From Address', config('mail.from.address')], + ['From Name', config('mail.from.name')], + ], 'compact'); + } else { + $this->table([], [ + ['Driver', $driver], + ['From Address', config('mail.from.address')], + ['From Name', config('mail.from.name')], + ], 'compact'); + } + + $this->output->title('Backup Configuration'); + $driver = config('backups.default'); + if ($driver === 's3') { + $this->table([], [ + ['Driver', $driver], + ['Region', config("backups.disks.$driver.region")], + ['Bucket', config("backups.disks.$driver.bucket")], + ['Endpoint', config("backups.disks.$driver.endpoint")], + ['Use path style endpoint', config("backups.disks.$driver.use_path_style_endpoint") ? 'Yes' : 'No'], + ], 'compact'); + } else { + $this->table([], [ + ['Driver', $driver], + ], 'compact'); + } } /**