Merge branch 'main' into lance/phpstan-return-types

This commit is contained in:
Lance Pioch 2024-10-20 11:39:04 -04:00 committed by GitHub
commit 81c75f7966
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 130 additions and 67 deletions

View File

@ -11,11 +11,8 @@ class CheckEggUpdatesCommand extends Command
{ {
protected $signature = 'p:egg:check-updates'; protected $signature = 'p:egg:check-updates';
public function handle(): void public function handle(EggExporterService $exporterService): void
{ {
/** @var EggExporterService $exporterService */
$exporterService = app(EggExporterService::class);
$eggs = Egg::all(); $eggs = Egg::all();
foreach ($eggs as $egg) { foreach ($eggs as $egg) {
try { try {

View File

@ -64,6 +64,7 @@ class DisplayException extends PanelException implements HttpExceptionInterface
return response()->json(Handler::toArray($this), $this->getStatusCode(), $this->getHeaders()); return response()->json(Handler::toArray($this), $this->getStatusCode(), $this->getHeaders());
} }
// @phpstan-ignore-next-line
app(AlertsMessageBag::class)->danger($this->getMessage())->flash(); app(AlertsMessageBag::class)->danger($this->getMessage())->flash();
return redirect()->back()->withInput(); return redirect()->back()->withInput();

View File

@ -273,6 +273,7 @@ class Handler extends ExceptionHandler
*/ */
public static function toArray(\Throwable $e): array public static function toArray(\Throwable $e): array
{ {
// @phpstan-ignore-next-line
return (new self(app()))->convertExceptionToArray($e); return (new self(app()))->convertExceptionToArray($e);
} }
} }

View File

@ -28,16 +28,20 @@ class Dashboard extends Page
public string $activeTab = 'nodes'; public string $activeTab = 'nodes';
private SoftwareVersionService $softwareVersionService;
public function mount(SoftwareVersionService $softwareVersionService): void
{
$this->softwareVersionService = $softwareVersionService;
}
public function getViewData(): array public function getViewData(): array
{ {
/** @var SoftwareVersionService $softwareVersionService */
$softwareVersionService = app(SoftwareVersionService::class);
return [ return [
'inDevelopment' => config('app.version') === 'canary', 'inDevelopment' => config('app.version') === 'canary',
'version' => $softwareVersionService->versionData()['version'], 'version' => $this->softwareVersionService->versionData()['version'],
'latestVersion' => $softwareVersionService->getPanel(), 'latestVersion' => $this->softwareVersionService->getPanel(),
'isLatest' => $softwareVersionService->isLatestPanel(), 'isLatest' => $this->softwareVersionService->isLatestPanel(),
'eggsCount' => Egg::query()->count(), 'eggsCount' => Egg::query()->count(),
'nodesList' => ListNodes::getUrl(), 'nodesList' => ListNodes::getUrl(),
'nodesCount' => Node::query()->count(), 'nodesCount' => Node::query()->count(),
@ -67,7 +71,7 @@ class Dashboard extends Page
CreateAction::make() CreateAction::make()
->label(trans('dashboard/index.sections.intro-support.button_donate')) ->label(trans('dashboard/index.sections.intro-support.button_donate'))
->icon('tabler-cash') ->icon('tabler-cash')
->url($softwareVersionService->getDonations(), true) ->url($this->softwareVersionService->getDonations(), true)
->color('success'), ->color('success'),
], ],
'helpActions' => [ 'helpActions' => [

View File

@ -2,6 +2,7 @@
namespace App\Filament\Pages\Installer; namespace App\Filament\Pages\Installer;
use App\Filament\Pages\Dashboard;
use App\Filament\Pages\Installer\Steps\AdminUserStep; use App\Filament\Pages\Installer\Steps\AdminUserStep;
use App\Filament\Pages\Installer\Steps\CompletedStep; use App\Filament\Pages\Installer\Steps\CompletedStep;
use App\Filament\Pages\Installer\Steps\DatabaseStep; use App\Filament\Pages\Installer\Steps\DatabaseStep;
@ -13,7 +14,6 @@ use App\Services\Users\UserCreationService;
use App\Traits\CheckMigrationsTrait; use App\Traits\CheckMigrationsTrait;
use App\Traits\EnvironmentWriterTrait; use App\Traits\EnvironmentWriterTrait;
use Exception; use Exception;
use Filament\Facades\Filament;
use Filament\Forms\Components\Actions\Action; use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\Wizard; use Filament\Forms\Components\Wizard;
use Filament\Forms\Concerns\InteractsWithForms; use Filament\Forms\Concerns\InteractsWithForms;
@ -104,7 +104,7 @@ class PanelInstaller extends SimplePage implements HasForms
auth()->guard()->login($this->user, true); auth()->guard()->login($this->user, true);
// Redirect to admin panel // Redirect to admin panel
return redirect(Filament::getPanel('admin')->getUrl()); return redirect(Dashboard::getUrl());
} }
public function writeToEnv(string $key): void public function writeToEnv(string $key): void
@ -160,12 +160,12 @@ class PanelInstaller extends SimplePage implements HasForms
} }
} }
public function createAdminUser(): void public function createAdminUser(UserCreationService $userCreationService): void
{ {
try { try {
$userData = array_get($this->data, 'user'); $userData = array_get($this->data, 'user');
$userData['root_admin'] = true; $userData['root_admin'] = true;
$this->user = app(UserCreationService::class)->handle($userData); $this->user = $userCreationService->handle($userData);
} catch (Exception $exception) { } catch (Exception $exception) {
report($exception); report($exception);

View File

@ -3,6 +3,7 @@
namespace App\Filament\Pages\Installer\Steps; namespace App\Filament\Pages\Installer\Steps;
use App\Filament\Pages\Installer\PanelInstaller; use App\Filament\Pages\Installer\PanelInstaller;
use App\Services\Users\UserCreationService;
use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Wizard\Step; use Filament\Forms\Components\Wizard\Step;
@ -28,6 +29,6 @@ class AdminUserStep
->password() ->password()
->revealable(), ->revealable(),
]) ])
->afterValidation(fn () => $installer->createAdminUser()); ->afterValidation(fn (UserCreationService $service) => $installer->createAdminUser($service));
} }
} }

View File

@ -18,6 +18,8 @@ use PDOException;
class CreateDatabaseHost extends CreateRecord class CreateDatabaseHost extends CreateRecord
{ {
private HostCreationService $service;
protected static string $resource = DatabaseHostResource::class; protected static string $resource = DatabaseHostResource::class;
protected ?string $heading = 'Database Hosts'; protected ?string $heading = 'Database Hosts';
@ -26,6 +28,11 @@ class CreateDatabaseHost extends CreateRecord
protected ?string $subheading = '(database servers that can have individual databases)'; protected ?string $subheading = '(database servers that can have individual databases)';
public function boot(HostCreationService $service)
{
$this->service = $service;
}
public function form(Form $form): Form public function form(Form $form): Form
{ {
return $form return $form
@ -96,7 +103,7 @@ class CreateDatabaseHost extends CreateRecord
protected function handleRecordCreation(array $data): Model protected function handleRecordCreation(array $data): Model
{ {
return resolve(HostCreationService::class)->handle($data); return $this->service->handle($data);
} }
public function exception(Exception $e, Closure $stopPropagation): void public function exception(Exception $e, Closure $stopPropagation): void

View File

@ -23,6 +23,13 @@ class EditDatabaseHost extends EditRecord
{ {
protected static string $resource = DatabaseHostResource::class; protected static string $resource = DatabaseHostResource::class;
private HostUpdateService $hostUpdateService;
public function boot(HostUpdateService $hostUpdateService)
{
$this->hostUpdateService = $hostUpdateService;
}
public function form(Form $form): Form public function form(Form $form): Form
{ {
return $form return $form
@ -105,7 +112,7 @@ class EditDatabaseHost extends EditRecord
return $record; return $record;
} }
return resolve(HostUpdateService::class)->handle($record, $data); return $this->hostUpdateService->handle($record, $data);
} }
public function exception(Exception $e, Closure $stopPropagation): void public function exception(Exception $e, Closure $stopPropagation): void

View File

@ -280,10 +280,7 @@ class EditEgg extends EditRecord
->contained(false), ->contained(false),
]) ])
->action(function (array $data, Egg $egg): void { ->action(function (array $data, Egg $egg, EggImporterService $eggImportService): void {
/** @var EggImporterService $eggImportService */
$eggImportService = resolve(EggImporterService::class);
if (!empty($data['egg'])) { if (!empty($data['egg'])) {
try { try {
$eggImportService->fromFile($data['egg'], $egg); $eggImportService->fromFile($data['egg'], $egg);

View File

@ -66,9 +66,10 @@ class ListEggs extends ListRecords
->modalDescription('If you made any changes to the egg they will be overwritten!') ->modalDescription('If you made any changes to the egg they will be overwritten!')
->modalIconColor('danger') ->modalIconColor('danger')
->modalSubmitAction(fn (Actions\StaticAction $action) => $action->color('danger')) ->modalSubmitAction(fn (Actions\StaticAction $action) => $action->color('danger'))
->action(function (Egg $egg) { ->action(function (Egg $egg, EggImporterService $eggImporterService) {
try { try {
app(EggImporterService::class)->fromUrl($egg->update_url, $egg); $eggImporterService->fromUrl($egg->update_url, $egg);
cache()->forget("eggs.{$egg->uuid}.update"); cache()->forget("eggs.{$egg->uuid}.update");
} catch (Exception $exception) { } catch (Exception $exception) {
Notification::make() Notification::make()
@ -129,10 +130,7 @@ class ListEggs extends ListRecords
->contained(false), ->contained(false),
]) ])
->action(function (array $data): void { ->action(function (array $data, EggImporterService $eggImportService): void {
/** @var EggImporterService $eggImportService */
$eggImportService = resolve(EggImporterService::class);
if (!empty($data['egg'])) { if (!empty($data['egg'])) {
/** @var TemporaryUploadedFile[] $eggFile */ /** @var TemporaryUploadedFile[] $eggFile */
$eggFile = $data['egg']; $eggFile = $data['egg'];

View File

@ -398,7 +398,7 @@ class CreateNode extends CreateRecord
protected function getRedirectUrlParameters(): array protected function getRedirectUrlParameters(): array
{ {
return [ return [
'tab' => '-configuration-tab', 'tab' => '-configuration-file-tab',
]; ];
} }

View File

@ -148,7 +148,7 @@ class AllocationsRelationManager extends RelationManager
->splitKeys(['Tab', ' ', ',']) ->splitKeys(['Tab', ' ', ','])
->required(), ->required(),
]) ])
->action(fn (array $data) => resolve(AssignmentService::class)->handle($this->getOwnerRecord(), $data)), ->action(fn (array $data, AssignmentService $service) => $service->handle($this->getOwnerRecord(), $data)),
]) ])
->bulkActions([ ->bulkActions([
BulkActionGroup::make([ BulkActionGroup::make([

View File

@ -49,6 +49,13 @@ class CreateServer extends CreateRecord
public ?Node $node = null; public ?Node $node = null;
private ServerCreationService $serverCreationService;
public function boot(ServerCreationService $serverCreationService)
{
$this->serverCreationService = $serverCreationService;
}
public function form(Form $form): Form public function form(Form $form): Form
{ {
return $form return $form
@ -118,8 +125,9 @@ class CreateServer extends CreateRecord
->hintIconTooltip('Providing a user password is optional. New user email will prompt users to create a password the first time they login.') ->hintIconTooltip('Providing a user password is optional. New user email will prompt users to create a password the first time they login.')
->password(), ->password(),
]) ])
->createOptionUsing(function ($data) { ->createOptionUsing(function ($data, UserCreationService $service) {
resolve(UserCreationService::class)->handle($data); $service->handle($data);
$this->refreshForm(); $this->refreshForm();
}) })
->required(), ->required(),
@ -262,9 +270,9 @@ class CreateServer extends CreateRecord
->splitKeys(['Tab', ' ', ',']) ->splitKeys(['Tab', ' ', ','])
->required(), ->required(),
]) ])
->createOptionUsing(function (array $data, Get $get): int { ->createOptionUsing(function (array $data, Get $get, AssignmentService $assignmentService): int {
return collect( return collect(
resolve(AssignmentService::class)->handle(Node::find($get('node_id')), $data) $assignmentService->handle(Node::find($get('node_id')), $data)
)->first(); )->first();
}) })
->required(), ->required(),
@ -825,10 +833,7 @@ class CreateServer extends CreateRecord
{ {
$data['allocation_additional'] = collect($data['allocation_additional'])->filter()->all(); $data['allocation_additional'] = collect($data['allocation_additional'])->filter()->all();
/** @var ServerCreationService $service */ return $this->serverCreationService->handle($data);
$service = resolve(ServerCreationService::class);
return $service->handle($data);
} }
private function shouldHideComponent(Get $get, Component $component): bool private function shouldHideComponent(Get $get, Component $component): bool

View File

@ -749,8 +749,8 @@ class EditServer extends EditRecord
->color('danger') ->color('danger')
->label('Delete') ->label('Delete')
->requiresConfirmation() ->requiresConfirmation()
->action(function (Server $server) { ->action(function (Server $server, ServerDeletionService $service) {
resolve(ServerDeletionService::class)->handle($server); $service->handle($server);
return redirect(ListServers::getUrl()); return redirect(ListServers::getUrl());
}) })

View File

@ -144,7 +144,7 @@ class AllocationsRelationManager extends RelationManager
->splitKeys(['Tab', ' ', ',']) ->splitKeys(['Tab', ' ', ','])
->required(), ->required(),
]) ])
->action(fn (array $data) => resolve(AssignmentService::class)->handle($this->getOwnerRecord()->node, $data, $this->getOwnerRecord())), ->action(fn (array $data, AssignmentService $service) => $service->handle($this->getOwnerRecord()->node, $data, $this->getOwnerRecord())),
AssociateAction::make() AssociateAction::make()
->multiple() ->multiple()
->associateAnother(false) ->associateAnother(false)

View File

@ -40,6 +40,13 @@ use Illuminate\Validation\Rules\Password;
*/ */
class EditProfile extends \Filament\Pages\Auth\EditProfile class EditProfile extends \Filament\Pages\Auth\EditProfile
{ {
private ToggleTwoFactorService $toggleTwoFactorService;
public function boot(ToggleTwoFactorService $toggleTwoFactorService): void
{
$this->toggleTwoFactorService = $toggleTwoFactorService;
}
protected function getForms(): array protected function getForms(): array
{ {
return [ return [
@ -108,7 +115,7 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
Tab::make('2FA') Tab::make('2FA')
->icon('tabler-shield-lock') ->icon('tabler-shield-lock')
->schema(function () { ->schema(function (TwoFactorSetupService $setupService) {
if ($this->getUser()->use_totp) { if ($this->getUser()->use_totp) {
return [ return [
Placeholder::make('2fa-already-enabled') Placeholder::make('2fa-already-enabled')
@ -126,8 +133,6 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
->helperText('Enter your current 2FA code to disable Two Factor Authentication'), ->helperText('Enter your current 2FA code to disable Two Factor Authentication'),
]; ];
} }
/** @var TwoFactorSetupService */
$setupService = app(TwoFactorSetupService::class);
['image_url_data' => $url, 'secret' => $secret] = cache()->remember( ['image_url_data' => $url, 'secret' => $secret] = cache()->remember(
"users.{$this->getUser()->id}.2fa.state", "users.{$this->getUser()->id}.2fa.state",
@ -283,20 +288,14 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
} }
if ($token = $data['2facode'] ?? null) { if ($token = $data['2facode'] ?? null) {
/** @var ToggleTwoFactorService $service */ $tokens = $this->toggleTwoFactorService->handle($record, $token, true);
$service = resolve(ToggleTwoFactorService::class);
$tokens = $service->handle($record, $token, true);
cache()->set("users.$record->id.2fa.tokens", implode("\n", $tokens), now()->addSeconds(15)); cache()->set("users.$record->id.2fa.tokens", implode("\n", $tokens), now()->addSeconds(15));
$this->redirectRoute('filament.admin.auth.profile', ['tab' => '-2fa-tab']); $this->redirectRoute('filament.admin.auth.profile', ['tab' => '-2fa-tab']);
} }
if ($token = $data['2fa-disable-code'] ?? null) { if ($token = $data['2fa-disable-code'] ?? null) {
/** @var ToggleTwoFactorService $service */ $this->toggleTwoFactorService->handle($record, $token, false);
$service = resolve(ToggleTwoFactorService::class);
$service->handle($record, $token, false);
cache()->forget("users.$record->id.2fa.state"); cache()->forget("users.$record->id.2fa.state");
} }

View File

@ -110,13 +110,11 @@ class ListUsers extends ListRecords
]), ]),
]) ])
->successRedirectUrl(route('filament.admin.resources.users.index')) ->successRedirectUrl(route('filament.admin.resources.users.index'))
->action(function (array $data) { ->action(function (array $data, UserCreationService $creationService) {
$roles = $data['roles']; $roles = $data['roles'];
$roles = collect($roles)->map(fn ($role) => Role::findById($role)); $roles = collect($roles)->map(fn ($role) => Role::findById($role));
unset($data['roles']); unset($data['roles']);
/** @var UserCreationService $creationService */
$creationService = resolve(UserCreationService::class);
$user = $creationService->handle($data); $user = $creationService->handle($data);
$user->syncRoles($roles); $user->syncRoles($roles);

View File

@ -32,18 +32,18 @@ class ServersRelationManager extends RelationManager
) )
->label('Suspend All Servers') ->label('Suspend All Servers')
->color('warning') ->color('warning')
->action(function () use ($user) { ->action(function (SuspensionService $suspensionService) use ($user) {
foreach ($user->servers()->whereNot('status', ServerState::Suspended)->get() as $server) { foreach ($user->servers()->whereNot('status', ServerState::Suspended)->get() as $server) {
resolve(SuspensionService::class)->toggle($server); $suspensionService->toggle($server);
} }
}), }),
Actions\Action::make('toggleUnsuspend') Actions\Action::make('toggleUnsuspend')
->hidden(fn () => $user->servers()->where('status', ServerState::Suspended)->count() === 0) ->hidden(fn () => $user->servers()->where('status', ServerState::Suspended)->count() === 0)
->label('Unsuspend All Servers') ->label('Unsuspend All Servers')
->color('primary') ->color('primary')
->action(function () use ($user) { ->action(function (SuspensionService $suspensionService) use ($user) {
foreach ($user->servers()->where('status', ServerState::Suspended)->get() as $server) { foreach ($user->servers()->where('status', ServerState::Suspended)->get() as $server) {
resolve(SuspensionService::class)->toggle($server, SuspensionService::ACTION_UNSUSPEND); $suspensionService->toggle($server, SuspensionService::ACTION_UNSUSPEND);
} }
}), }),
]) ])

View File

@ -3,23 +3,26 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Illuminate\Foundation\Application;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use App\Events\Auth\FailedCaptcha; use App\Events\Auth\FailedCaptcha;
use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpException;
class VerifyReCaptcha readonly class VerifyReCaptcha
{ {
/** public function __construct(private Application $app)
* Handle an incoming request. {
*/
}
public function handle(Request $request, \Closure $next): mixed public function handle(Request $request, \Closure $next): mixed
{ {
if (!config('recaptcha.enabled')) { if (!config('recaptcha.enabled')) {
return $next($request); return $next($request);
} }
if (app()->isLocal()) { if ($this->app->isLocal()) {
return $next($request); return $next($request);
} }

View File

@ -295,6 +295,7 @@ class Node extends Model
{ {
return once(function () { return once(function () {
try { try {
// @phpstan-ignore-next-line
return resolve(DaemonConfigurationRepository::class) return resolve(DaemonConfigurationRepository::class)
->setNode($this) ->setNode($this)
->getSystemInformation(connectTimeout: 3); ->getSystemInformation(connectTimeout: 3);

View File

@ -0,0 +1,38 @@
<?php
namespace App\PHPStan;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
class ForbiddenGlobalFunctionsRule implements Rule
{
private array $forbiddenFunctions;
public function __construct(array $forbiddenFunctions = ['app', 'resolve'])
{
$this->forbiddenFunctions = $forbiddenFunctions;
}
public function getNodeType(): string
{
return FuncCall::class;
}
public function processNode(Node $node, Scope $scope): array
{
/** @var FuncCall $node */
if ($node->name instanceof Node\Name) {
$functionName = (string) $node->name;
if (in_array($functionName, $this->forbiddenFunctions, true)) {
return [
sprintf('Usage of global function "%s" is forbidden.', $functionName),
];
}
}
return [];
}
}

View File

@ -13,6 +13,7 @@ use Dedoc\Scramble\Support\Generator\SecurityScheme;
use Filament\Support\Colors\Color; use Filament\Support\Colors\Color;
use Filament\Support\Facades\FilamentColor; use Filament\Support\Facades\FilamentColor;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Foundation\Application;
use Illuminate\Pagination\Paginator; use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Broadcast; use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
@ -29,7 +30,7 @@ class AppServiceProvider extends ServiceProvider
/** /**
* Bootstrap any application services. * Bootstrap any application services.
*/ */
public function boot(): void public function boot(Application $app): void
{ {
// TODO: remove when old admin area gets yeeted // TODO: remove when old admin area gets yeeted
View::share('appVersion', config('app.version')); View::share('appVersion', config('app.version'));
@ -64,7 +65,7 @@ class AppServiceProvider extends ServiceProvider
->asJson() ->asJson()
->withToken($node->daemon_token) ->withToken($node->daemon_token)
->withHeaders($headers) ->withHeaders($headers)
->withOptions(['verify' => (bool) app()->environment('production')]) ->withOptions(['verify' => (bool) $app->environment('production')])
->timeout(config('panel.guzzle.timeout')) ->timeout(config('panel.guzzle.timeout'))
->connectTimeout(config('panel.guzzle.connect_timeout')) ->connectTimeout(config('panel.guzzle.connect_timeout'))
->baseUrl($node->getConnectionAddress()) ->baseUrl($node->getConnectionAddress())

View File

@ -12,7 +12,7 @@ trait CheckMigrationsTrait
protected function hasCompletedMigrations(): bool protected function hasCompletedMigrations(): bool
{ {
/** @var Migrator $migrator */ /** @var Migrator $migrator */
$migrator = app()->make('migrator'); $migrator = app()->make('migrator'); // @phpstan-ignore-line
$files = $migrator->getMigrationFiles(database_path('migrations')); $files = $migrator->getMigrationFiles(database_path('migrations'));

View File

@ -51,6 +51,7 @@ trait AvailableLanguages
*/ */
private function getFilesystemInstance(): Filesystem private function getFilesystemInstance(): Filesystem
{ {
// @phpstan-ignore-next-line
return $this->filesystem = $this->filesystem ?: app()->make(Filesystem::class); return $this->filesystem = $this->filesystem ?: app()->make(Filesystem::class);
} }
} }

View File

@ -54,6 +54,7 @@ abstract class BaseTransformer extends TransformerAbstract
*/ */
public static function fromRequest(Request $request): self public static function fromRequest(Request $request): self
{ {
// @phpstan-ignore-next-line
return app(static::class)->setRequest($request); return app(static::class)->setRequest($request);
} }

View File

@ -1,6 +1,9 @@
includes: includes:
- vendor/larastan/larastan/extension.neon - vendor/larastan/larastan/extension.neon
rules:
- App\PHPStan\ForbiddenGlobalFunctionsRule
parameters: parameters:
paths: paths: