Enforce DI

This commit is contained in:
Lance Pioch 2024-10-19 17:22:03 -04:00
parent 207d875df8
commit 54ea55d426
25 changed files with 128 additions and 69 deletions

View File

@ -11,11 +11,8 @@ class CheckEggUpdatesCommand extends Command
{
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();
foreach ($eggs as $egg) {
try {

View File

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

View File

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

View File

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

View File

@ -159,12 +159,12 @@ class PanelInstaller extends SimplePage implements HasForms
}
}
public function createAdminUser(): void
public function createAdminUser(UserCreationService $userCreationService): void
{
try {
$userData = array_get($this->data, 'user');
$userData['root_admin'] = true;
$this->user = app(UserCreationService::class)->handle($userData);
$this->user = $userCreationService->handle($userData);
} catch (Exception $exception) {
report($exception);

View File

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

View File

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

View File

@ -21,6 +21,13 @@ class EditDatabaseHost extends EditRecord
{
protected static string $resource = DatabaseHostResource::class;
private HostUpdateService $hostUpdateService;
public function boot(HostUpdateService $hostUpdateService)
{
$this->hostUpdateService = $hostUpdateService;
}
public function form(Form $form): Form
{
return $form
@ -99,7 +106,7 @@ class EditDatabaseHost extends EditRecord
protected function handleRecordUpdate($record, array $data): Model
{
return resolve(HostUpdateService::class)->handle($record->id, $data);
return $this->hostUpdateService->handle($record->id, $data);
}
public function exception($e, $stopPropagation): void

View File

@ -280,10 +280,7 @@ class EditEgg extends EditRecord
->contained(false),
])
->action(function (array $data, Egg $egg): void {
/** @var EggImporterService $eggImportService */
$eggImportService = resolve(EggImporterService::class);
->action(function (array $data, Egg $egg, EggImporterService $eggImportService): void {
if (!empty($data['egg'])) {
try {
$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!')
->modalIconColor('danger')
->modalSubmitAction(fn (Actions\StaticAction $action) => $action->color('danger'))
->action(function (Egg $egg) {
->action(function (Egg $egg, EggImporterService $eggImporterService) {
try {
app(EggImporterService::class)->fromUrl($egg->update_url, $egg);
$eggImporterService->fromUrl($egg->update_url, $egg);
cache()->forget("eggs.{$egg->uuid}.update");
} catch (Exception $exception) {
Notification::make()
@ -129,10 +130,7 @@ class ListEggs extends ListRecords
->contained(false),
])
->action(function (array $data): void {
/** @var EggImporterService $eggImportService */
$eggImportService = resolve(EggImporterService::class);
->action(function (array $data, EggImporterService $eggImportService): void {
if (!empty($data['egg'])) {
/** @var TemporaryUploadedFile[] $eggFile */
$eggFile = $data['egg'];

View File

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

View File

@ -49,6 +49,13 @@ class CreateServer extends CreateRecord
public ?Node $node = null;
private ServerCreationService $serverCreationService;
public function boot(ServerCreationService $serverCreationService)
{
$this->serverCreationService = $serverCreationService;
}
public function form(Form $form): 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.')
->password(),
])
->createOptionUsing(function ($data) {
resolve(UserCreationService::class)->handle($data);
->createOptionUsing(function ($data, UserCreationService $service) {
$service->handle($data);
$this->refreshForm();
})
->required(),
@ -262,9 +270,9 @@ class CreateServer extends CreateRecord
->splitKeys(['Tab', ' ', ','])
->required(),
])
->createOptionUsing(function (array $data, Get $get): int {
->createOptionUsing(function (array $data, Get $get, AssignmentService $assignmentService): int {
return collect(
resolve(AssignmentService::class)->handle(Node::find($get('node_id')), $data)
$assignmentService->handle(Node::find($get('node_id')), $data)
)->first();
})
->required(),
@ -825,10 +833,7 @@ class CreateServer extends CreateRecord
{
$data['allocation_additional'] = collect($data['allocation_additional'])->filter()->all();
/** @var ServerCreationService $service */
$service = resolve(ServerCreationService::class);
return $service->handle($data);
return $this->serverCreationService->handle($data);
}
private function shouldHideComponent(Get $get, Component $component): bool

View File

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

View File

@ -144,7 +144,7 @@ class AllocationsRelationManager extends RelationManager
->splitKeys(['Tab', ' ', ','])
->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()
->multiple()
->associateAnother(false)

View File

@ -38,6 +38,13 @@ use Illuminate\Validation\Rules\Password;
*/
class EditProfile extends \Filament\Pages\Auth\EditProfile
{
private ToggleTwoFactorService $toggleTwoFactorService;
public function boot(ToggleTwoFactorService $toggleTwoFactorService): void
{
$this->toggleTwoFactorService = $toggleTwoFactorService;
}
protected function getForms(): array
{
return [
@ -106,7 +113,7 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
Tab::make('2FA')
->icon('tabler-shield-lock')
->schema(function () {
->schema(function (TwoFactorSetupService $setupService) {
if ($this->getUser()->use_totp) {
return [
Placeholder::make('2fa-already-enabled')
@ -124,8 +131,6 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
->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(
"users.{$this->getUser()->id}.2fa.state",
@ -277,20 +282,14 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
protected function handleRecordUpdate($record, $data): Model
{
if ($token = $data['2facode'] ?? null) {
/** @var ToggleTwoFactorService $service */
$service = resolve(ToggleTwoFactorService::class);
$tokens = $service->handle($record, $token, true);
$tokens = $this->toggleTwoFactorService->handle($record, $token, true);
cache()->set("users.$record->id.2fa.tokens", implode("\n", $tokens), now()->addSeconds(15));
$this->redirectRoute('filament.admin.auth.profile', ['tab' => '-2fa-tab']);
}
if ($token = $data['2fa-disable-code'] ?? null) {
/** @var ToggleTwoFactorService $service */
$service = resolve(ToggleTwoFactorService::class);
$service->handle($record, $token, false);
$this->toggleTwoFactorService->handle($record, $token, false);
cache()->forget("users.$record->id.2fa.state");
}

View File

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

View File

@ -32,18 +32,18 @@ class ServersRelationManager extends RelationManager
)
->label('Suspend All Servers')
->color('warning')
->action(function () use ($user) {
->action(function (SuspensionService $suspensionService) use ($user) {
foreach ($user->servers()->whereNot('status', ServerState::Suspended)->get() as $server) {
resolve(SuspensionService::class)->toggle($server);
$suspensionService->toggle($server);
}
}),
Actions\Action::make('toggleUnsuspend')
->hidden(fn () => $user->servers()->where('status', ServerState::Suspended)->count() === 0)
->label('Unsuspend All Servers')
->color('primary')
->action(function () use ($user) {
->action(function (SuspensionService $suspensionService) use ($user) {
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;
use GuzzleHttp\Client;
use Illuminate\Foundation\Application;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Events\Auth\FailedCaptcha;
use Symfony\Component\HttpKernel\Exception\HttpException;
class VerifyReCaptcha
readonly class VerifyReCaptcha
{
/**
* Handle an incoming request.
*/
public function __construct(private Application $app)
{
}
public function handle(Request $request, \Closure $next): mixed
{
if (!config('recaptcha.enabled')) {
return $next($request);
}
if (app()->isLocal()) {
if ($this->app->isLocal()) {
return $next($request);
}

View File

@ -294,6 +294,7 @@ class Node extends Model
{
return once(function () {
try {
// @phpstan-ignore-next-line
return resolve(DaemonConfigurationRepository::class)
->setNode($this)
->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\Facades\FilamentColor;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Foundation\Application;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Event;
@ -29,7 +30,7 @@ class AppServiceProvider extends ServiceProvider
/**
* Bootstrap any application services.
*/
public function boot(): void
public function boot(Application $app): void
{
// TODO: remove when old admin area gets yeeted
View::share('appVersion', config('app.version'));
@ -64,7 +65,7 @@ class AppServiceProvider extends ServiceProvider
->asJson()
->withToken($node->daemon_token)
->withHeaders($headers)
->withOptions(['verify' => (bool) app()->environment('production')])
->withOptions(['verify' => (bool) $app->environment('production')])
->timeout(config('panel.guzzle.timeout'))
->connectTimeout(config('panel.guzzle.connect_timeout'))
->baseUrl($node->getConnectionAddress())

View File

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

View File

@ -51,6 +51,7 @@ trait AvailableLanguages
*/
private function getFilesystemInstance(): Filesystem
{
// @phpstan-ignore-next-line
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
{
// @phpstan-ignore-next-line
return app(static::class)->setRequest($request);
}

View File

@ -1,6 +1,9 @@
includes:
- vendor/larastan/larastan/extension.neon
rules:
- App\PHPStan\ForbiddenGlobalFunctionsRule
parameters:
paths:
@ -13,8 +16,4 @@ parameters:
# Prologue\Alerts defines its methods from its configuration file dynamically
- '#^Call to an undefined method Prologue\\Alerts\\AlertsMessageBag::(danger|success|info|warning)\(\)\.$#'
# excludePaths:
# - ./*/*/FileToBeExcluded.php
#
# checkMissingIterableValueType: false
# checkMissingIterableValueType: false