From a75450a98a28ef5db40e353574985238ce9938e9 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Mon, 19 Sep 2022 10:36:06 +0300 Subject: [PATCH 1/9] Add SF 3.0 support --- README.md | 41 ++++--- app.php | 32 ++--- app/src/App.php | 96 +++++++++------ app/src/Bootloader/AdminBootloader.php | 6 +- app/src/Bootloader/AppBootloader.php | 14 ++- .../Bootloader/ExceptionHandlerBootloader.php | 48 ++++++++ .../Bootloader/LocaleSelectorBootloader.php | 27 ----- app/src/Bootloader/LoggingBootloader.php | 15 +-- app/src/Bootloader/RoutesBootloader.php | 39 ++++++ app/src/Bootloader/SecurityBootloader.php | 14 +-- app/src/Command/User/CreateCommand.php | 13 +- app/src/Command/User/SeedCommand.php | 21 ++-- app/src/Controller/AuthController.php | 6 +- .../Controller/Keeper/DashboardController.php | 7 -- .../Controller/Keeper/ProfileController.php | 11 -- .../Controller/Keeper/ShowcaseController.php | 7 -- app/src/Controller/Keeper/UserController.php | 2 +- app/src/ErrorHandler/ViewRenderer.php | 61 ++++++++++ app/src/Exception/CollisionRenderer.php | 52 ++++++++ app/src/Exception/Handler.php | 16 +++ app/src/Interceptor/ValidationInterceptor.php | 33 ++++++ app/src/Job/Ping.php | 6 +- app/src/Repository/UserRepository.php | 19 +-- app/src/Request/Auth/LoginRequest.php | 86 ++++++-------- app/src/Request/Auth/LogoutRequest.php | 39 +++--- .../Request/Keeper/Profile/UpdateRequest.php | 112 +++++++++--------- app/src/Request/Keeper/User/CreateRequest.php | 111 +++++++++-------- app/src/Request/Keeper/User/RolesRequest.php | 55 ++++----- .../Keeper/User/UpdatePasswordRequest.php | 69 +++++------ app/src/Request/Keeper/User/UpdateRequest.php | 70 ++++++----- app/src/Security/PasswordHasher.php | 32 +---- .../Security/Rule/NotSelfOrOtherAdminRule.php | 19 +-- app/src/Security/Rule/NotSelfRule.php | 13 -- app/src/Security/Rule/OnlyNotAdminsRule.php | 7 -- app/src/Security/Rule/SelfRule.php | 13 -- .../Service/Exception/PersistException.php | 7 -- app/src/helpers.php | 36 ++++++ app/views/exception/403.dark.php | 21 ++++ app/views/exception/404.dark.php | 21 ++++ app/views/exception/500.dark.php | 21 ++++ app/views/exception/error.dark.php | 21 ++++ app/views/keeper/login.dark.php | 2 +- composer.json | 34 ++++-- 43 files changed, 780 insertions(+), 595 deletions(-) create mode 100644 app/src/Bootloader/ExceptionHandlerBootloader.php delete mode 100644 app/src/Bootloader/LocaleSelectorBootloader.php create mode 100644 app/src/Bootloader/RoutesBootloader.php create mode 100644 app/src/ErrorHandler/ViewRenderer.php create mode 100644 app/src/Exception/CollisionRenderer.php create mode 100644 app/src/Exception/Handler.php create mode 100644 app/src/Interceptor/ValidationInterceptor.php create mode 100644 app/src/helpers.php create mode 100644 app/views/exception/403.dark.php create mode 100644 app/views/exception/404.dark.php create mode 100644 app/views/exception/500.dark.php create mode 100644 app/views/exception/error.dark.php diff --git a/README.md b/README.md index 4a72ddc..3111e39 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Spiral Framework is a High-Performance PHP/Go Full-Stack framework and group of Server Requirements -------- Make sure that your server is configured with following PHP version and extensions: -* PHP 8.0+, 64bit +* PHP 8.1+, 64bit * *mb-string* extension * PDO Extension with desired database drivers (default SQLite) * For FrontEnd build yarn and nodejs are required. @@ -41,52 +41,54 @@ Demo Screenshot Installation -------- ``` -composer create-project spiral/app-keeper --stability dev +composer create-project spiral/app-keeper cd app-keeper yarn && yarn build ``` +> **Note** > Application server will be downloaded automatically (`php-curl` and `php-zip` required). Once the application is installed you can ensure that it was configured properly by executing: ``` -$ php app.php configure -vv +php app.php configure -vv ``` Migrate the database: ``` -$ php app.php migrate:init -$ php app.php migrate +php app.php migrate:init +php app.php migrate ``` Seed user accounts: ``` -$ php app.php user:seed +php app.php user:seed ``` Create super admin account: ``` -$ php app.php user:create {First-Name} {Last-Name} {email-address} {password} +php app.php user:create {First-Name} {Last-Name} {email-address} {password} ``` To start application server execute: ``` -$ ./rr serve -d +./rr serve -d ``` On Windows: ``` -$ ./rr.exe serve -d +./rr.exe serve -d ``` Application will be available on `http://localhost:8080`. Keeper control panel available at `http://localhost:8080/keeper`. +> **Note** > Read more about application server configuration [here](https://roadrunner.dev/docs). Make sure to turn `DEBUG` off in `.env` to enable view caching. Testing: @@ -94,7 +96,7 @@ Testing: To test an application: ```bash -$ ./vendor/bin/phpunit +./vendor/bin/phpunit ``` Cloning: @@ -102,16 +104,17 @@ Cloning: Make sure to properly configure project if you cloned the existing repository. ```bash -$ copy .env.sample .env -$ composer install -$ php app.php encrypt:key -m .env -$ php app.php configure -vv -$ php app.php migrate:init -$ php app.php migrate -$ ./vendor/bin/rr get -$ yarn build +copy .env.sample .env +composer install +php app.php encrypt:key -m .env +php app.php configure -vv +php app.php migrate:init +php app.php migrate +./vendor/bin/rr get +yarn build ``` +> **Note** > Make sure to create super-admin account. Docker: @@ -122,7 +125,7 @@ Requirements: Docker engine 19.03.0+ To launch Keeper in Docker create env file if needed. ```bash - copy .env.sample .env +copy .env.sample .env ``` Build and run for Linux and MacOS diff --git a/app.php b/app.php index 9926415..a4e9658 100644 --- a/app.php +++ b/app.php @@ -1,38 +1,30 @@ __DIR__]); +$app = App::create( + directories: ['root' => __DIR__], + exceptionHandler: \App\Exception\Handler::class +)->run(); -if ($app !== null) { - $code = (int)$app->serve(); - exit($code); +if ($app === null) { + exit(255); } + +$code = (int)$app->serve(); +exit($code); diff --git a/app/src/App.php b/app/src/App.php index e6c2c1b..7e55ef0 100644 --- a/app/src/App.php +++ b/app/src/App.php @@ -5,103 +5,125 @@ namespace App; use App\Bootloader; +use Spiral\Boot\Bootloader\CoreBootloader; use Spiral\Bootloader as Framework; +use Spiral\DataGrid\Bootloader\GridBootloader; +use Spiral\Distribution\Bootloader\DistributionBootloader; use Spiral\DotEnv\Bootloader as DotEnv; use Spiral\Framework\Kernel; use Spiral\Monolog\Bootloader as Monolog; use Spiral\Nyholm\Bootloader as Nyholm; use Spiral\Prototype\Bootloader as Prototype; use Spiral\Router\Bootloader as Router; +use Spiral\Sapi\Bootloader\SapiBootloader; use Spiral\Scaffolder\Bootloader as Scaffolder; use Spiral\Stempler\Bootloader as Stempler; +use Spiral\Storage\Bootloader\StorageBootloader; +use Spiral\Tokenizer\Bootloader\TokenizerBootloader; +use Spiral\Validation\Bootloader\ValidationBootloader; +use Spiral\Views\Bootloader\ViewsBootloader; use Spiral\Writeaway\Bootloader as Writeaway; use Spiral\Cycle\Bootloader as CycleBridge; use Spiral\RoadRunnerBridge\Bootloader as RoadRunnerBridge; class App extends Kernel { + protected const SYSTEM = [ + CoreBootloader::class, + TokenizerBootloader::class, + DotEnv\DotenvBootloader::class, + ]; + /* * List of components and extensions to be automatically registered * within system container on application start. */ protected const LOAD = [ - /** -- roadrunner -- */ + // Logging and exceptions handling + Monolog\MonologBootloader::class, + Bootloader\ExceptionHandlerBootloader::class, + + // Application specific logs + Bootloader\LoggingBootloader::class, + + // RoadRunner + RoadRunnerBridge\CacheBootloader::class, + RoadRunnerBridge\GRPCBootloader::class, RoadRunnerBridge\HttpBootloader::class, RoadRunnerBridge\QueueBootloader::class, - RoadRunnerBridge\CommandBootloader::class, + RoadRunnerBridge\RoadRunnerBootloader::class, - /* -- debug and profiling --*/ - DotEnv\DotenvBootloader::class, - Monolog\MonologBootloader::class, + // Core Services Framework\SnapshotsBootloader::class, - Framework\DebugBootloader::class, - Framework\Debug\LogCollectorBootloader::class, - Framework\Debug\HttpCollectorBootloader::class, - - /* -- application specific logging --*/ - Bootloader\LoggingBootloader::class, + Framework\I18nBootloader::class, - /* -- validation, security and encryption --*/ + // Security and validation Framework\Security\EncrypterBootloader::class, - Framework\Security\ValidationBootloader::class, + ValidationBootloader::class, Framework\Security\FiltersBootloader::class, Framework\Security\GuardBootloader::class, - /* -- HTTP --*/ + // HTTP extensions Nyholm\NyholmBootloader::class, - Framework\Http\ErrorHandlerBootloader::class, + Framework\Http\RouterBootloader::class, + Framework\Http\JsonPayloadsBootloader::class, Framework\Http\CookiesBootloader::class, Framework\Http\SessionBootloader::class, Framework\Http\CsrfBootloader::class, Framework\Http\PaginationBootloader::class, - Framework\Http\RouterBootloader::class, - Framework\Http\JsonPayloadsBootloader::class, + SapiBootloader::class, + Router\AnnotatedRoutesBootloader::class, - /* -- ORM and databases --*/ + // Databases CycleBridge\DatabaseBootloader::class, CycleBridge\MigrationsBootloader::class, - CycleBridge\DisconnectsBootloader::class, + // CycleBridge\DisconnectsBootloader::class, + + // ORM CycleBridge\SchemaBootloader::class, CycleBridge\CycleOrmBootloader::class, CycleBridge\AnnotatedBootloader::class, CycleBridge\CommandBootloader::class, + + // DataGrid + GridBootloader::class, + CycleBridge\DataGridBootloader::class, + + // Writeaway Writeaway\WriteawayBootloader::class, Writeaway\WriteawayCommandBootloader::class, Writeaway\WriteawayViewsBootloader::class, - /* -- stempler and views --*/ - Framework\Views\ViewsBootloader::class, + // Stempler and views + ViewsBootloader::class, Framework\Views\TranslatedCacheBootloader::class, Stempler\StemplerBootloader::class, Stempler\PrettyPrintBootloader::class, - /* -- security and auth context --*/ + // Security and auth context Framework\Auth\HttpAuthBootloader::class, CycleBridge\AuthTokensBootloader::class, Framework\Auth\SecurityActorBootloader::class, - /* -- other components --*/ - Framework\I18nBootloader::class, - Framework\Storage\StorageBootloader::class, - Framework\Distribution\DistributionBootloader::class, + // Other components + StorageBootloader::class, + DistributionBootloader::class, - /* -- data rendering --*/ - CycleBridge\DataGridBootloader::class, - - /* -- routes and middleware -- */ - Router\AnnotatedRoutesBootloader::class, - Bootloader\LocaleSelectorBootloader::class, - - /* -- security and admin panels --*/ + // Security and admin panels Bootloader\SecurityBootloader::class, Bootloader\AdminBootloader::class, - /* -- development helpers --*/ + // Development helpers Framework\CommandBootloader::class, - Prototype\PrototypeBootloader::class, Scaffolder\ScaffolderBootloader::class, - //App + // App + Bootloader\RoutesBootloader::class, Bootloader\AppBootloader::class, ]; + + protected const APP = [ + // fast code prototyping + Prototype\PrototypeBootloader::class, + ]; } diff --git a/app/src/Bootloader/AdminBootloader.php b/app/src/Bootloader/AdminBootloader.php index 30456ab..7dfe263 100644 --- a/app/src/Bootloader/AdminBootloader.php +++ b/app/src/Bootloader/AdminBootloader.php @@ -5,9 +5,9 @@ namespace App\Bootloader; use App\Controller\Keeper\DashboardController; -use Spiral\Cycle\DataGrid\Interceptor\GridInterceptor; +use App\Interceptor\ValidationInterceptor; +use Spiral\DataGrid\Interceptor\GridInterceptor; use Spiral\Cycle\Interceptor\CycleInterceptor; -use Spiral\Domain\FilterInterceptor; use Spiral\Domain\GuardInterceptor; use Spiral\Keeper\Bootloader as Keeper; use Spiral\Keeper\Bootloader\KeeperBootloader; @@ -26,7 +26,7 @@ class AdminBootloader extends KeeperBootloader protected const INTERCEPTORS = [ CycleInterceptor::class, GuardInterceptor::class, - FilterInterceptor::class, + ValidationInterceptor::class, GridInterceptor::class, ]; diff --git a/app/src/Bootloader/AppBootloader.php b/app/src/Bootloader/AppBootloader.php index 2c79ac5..65c65a1 100644 --- a/app/src/Bootloader/AppBootloader.php +++ b/app/src/Bootloader/AppBootloader.php @@ -4,13 +4,23 @@ namespace App\Bootloader; +use App\Interceptor\ValidationInterceptor; use App\Service\Writeaway\MetaProvider; -use Spiral\Boot\Bootloader\Bootloader; +use Spiral\Bootloader\DomainBootloader; +use Spiral\Core\CoreInterface; use Spiral\Writeaway\MetaProviderInterface; -class AppBootloader extends Bootloader +class AppBootloader extends DomainBootloader { + protected const SINGLETONS = [ + CoreInterface::class => [self::class, 'domainCore'] + ]; + protected const BINDINGS = [ MetaProviderInterface::class => MetaProvider::class, ]; + + protected const INTERCEPTORS = [ + ValidationInterceptor::class + ]; } diff --git a/app/src/Bootloader/ExceptionHandlerBootloader.php b/app/src/Bootloader/ExceptionHandlerBootloader.php new file mode 100644 index 0000000..748d561 --- /dev/null +++ b/app/src/Bootloader/ExceptionHandlerBootloader.php @@ -0,0 +1,48 @@ + EnvSuppressErrors::class, + RendererInterface::class => ViewRenderer::class, + ]; + + public function init(AbstractKernel $kernel): void + { + $kernel->running(static function (ExceptionHandler $handler): void { + // $handler->addRenderer(new ConsoleRenderer()); + $handler->addRenderer(new CollisionRenderer()); + $handler->addRenderer(new JsonRenderer()); + }); + } + + public function boot( + ExceptionHandlerInterface $handler, + LoggerReporter $logger, + FileReporter $files, + IgnitionRenderer $ignition + ): void { + $handler->addReporter($logger); + $handler->addReporter($files); + + $handler->addRenderer($ignition); + } +} diff --git a/app/src/Bootloader/LocaleSelectorBootloader.php b/app/src/Bootloader/LocaleSelectorBootloader.php deleted file mode 100644 index ee099af..0000000 --- a/app/src/Bootloader/LocaleSelectorBootloader.php +++ /dev/null @@ -1,27 +0,0 @@ -addMiddleware(LocaleSelector::class); - } -} diff --git a/app/src/Bootloader/LoggingBootloader.php b/app/src/Bootloader/LoggingBootloader.php index 62141d7..81ee88a 100644 --- a/app/src/Bootloader/LoggingBootloader.php +++ b/app/src/Bootloader/LoggingBootloader.php @@ -1,12 +1,5 @@ addHandler(ErrorHandlerMiddleware::class, $monolog->logRotate( @@ -30,7 +23,7 @@ public function boot(MonologBootloader $monolog): void )); // app level errors - $monolog->addHandler(LogFactory::DEFAULT, $monolog->logRotate( + $monolog->addHandler(MonologConfig::DEFAULT_CHANNEL, $monolog->logRotate( directory('runtime') . 'logs/error.log', Logger::ERROR, 25, @@ -38,7 +31,7 @@ public function boot(MonologBootloader $monolog): void )); // debug and info messages via global LoggerInterface - $monolog->addHandler(LogFactory::DEFAULT, $monolog->logRotate( + $monolog->addHandler(MonologConfig::DEFAULT_CHANNEL, $monolog->logRotate( directory('runtime') . 'logs/debug.log' )); } diff --git a/app/src/Bootloader/RoutesBootloader.php b/app/src/Bootloader/RoutesBootloader.php new file mode 100644 index 0000000..370390b --- /dev/null +++ b/app/src/Bootloader/RoutesBootloader.php @@ -0,0 +1,39 @@ +firstName = $this->argument('firstName'); @@ -42,5 +35,9 @@ protected function perform(): void $user->roles = 'super-admin'; $this->entities->save($user); + + $this->output->success('Super-Admin successfully created.'); + + return self::SUCCESS; } } diff --git a/app/src/Command/User/SeedCommand.php b/app/src/Command/User/SeedCommand.php index dbe1a83..f2bea8d 100644 --- a/app/src/Command/User/SeedCommand.php +++ b/app/src/Command/User/SeedCommand.php @@ -1,12 +1,5 @@ firstName = $faker->firstName; - $user->lastName = $faker->lastName; - $user->email = $faker->email; - $user->passwordHash = $this->passwords->hash($faker->password); + $user->firstName = $faker->firstName(); + $user->lastName = $faker->lastName(); + $user->email = $faker->email(); + $user->passwordHash = $this->passwords->hash($faker->password()); $user->roles = 'user'; $this->entities->save($user); } + + $this->output->success('Database seeding completed successfully.'); + + return self::SUCCESS; } } diff --git a/app/src/Controller/AuthController.php b/app/src/Controller/AuthController.php index 3ebb2f5..0ea876b 100644 --- a/app/src/Controller/AuthController.php +++ b/app/src/Controller/AuthController.php @@ -21,8 +21,8 @@ class AuthController #[Route(route: '/login', name: 'auth:login')] public function login(LoginRequest $request): array { - $user = $this->users->findByUsername($request->getUsername()); - if ($user === null || !$this->passwords->compare($request->getPassword(), $user->passwordHash)) { + $user = $this->users->findByUsername($request->username); + if ($user === null || !$this->passwords->compare($request->password, $user->passwordHash)) { return [ 'status' => 400, 'error' => 'No such user', @@ -46,7 +46,7 @@ public function login(LoginRequest $request): array #[Route(route: '/logout', name: 'auth:logout')] public function logout(LogoutRequest $logout): ResponseInterface { - if ($this->auth->getToken() === null || $this->auth->getToken()->getID() !== $logout->getToken()) { + if ($this->auth->getToken() === null || $this->auth->getToken()->getID() !== $logout->token) { throw new BadRequestException(); } diff --git a/app/src/Controller/Keeper/DashboardController.php b/app/src/Controller/Keeper/DashboardController.php index 7e0a174..053cdca 100644 --- a/app/src/Controller/Keeper/DashboardController.php +++ b/app/src/Controller/Keeper/DashboardController.php @@ -1,12 +1,5 @@ /update', methods: 'POST')] diff --git a/app/src/Controller/Keeper/ShowcaseController.php b/app/src/Controller/Keeper/ShowcaseController.php index 7d927bf..b1d0199 100644 --- a/app/src/Controller/Keeper/ShowcaseController.php +++ b/app/src/Controller/Keeper/ShowcaseController.php @@ -1,12 +1,5 @@ getHeaderLine('Accept'))->getAll(); + if ($acceptItems && $acceptItems[0]->getValue() === 'application/json') { + return $this->renderJson($code, $exception); + } + + return $this->renderView($code, $exception); + } + + private function renderJson(int $code, \Throwable $exception): ResponseInterface + { + $response = $this->responseFactory->createResponse($code); + + $response = $response->withHeader('Content-Type', 'application/json; charset=UTF-8'); + $response->getBody()->write(\json_encode(['status' => $code, 'error' => $exception->getMessage()])); + + return $response; + } + + private function renderView(int $code, \Throwable $exception): ResponseInterface + { + $response = $this->responseFactory->createResponse($code); + + try { + $view = $this->views->get(\sprintf(self::VIEW, $code)); + } catch (ViewException) { + $view = $this->views->get(self::GENERAL_VIEW); + } + + $content = $view->render(['code' => $code, 'exception' => $exception]); + $response->getBody()->write($content); + + return $response; + } +} diff --git a/app/src/Exception/CollisionRenderer.php b/app/src/Exception/CollisionRenderer.php new file mode 100644 index 0000000..23c9820 --- /dev/null +++ b/app/src/Exception/CollisionRenderer.php @@ -0,0 +1,52 @@ +handler = $provider->register()->getHandler(); + } + + public function render( + \Throwable $exception, + ?Verbosity $verbosity = Verbosity::BASIC, + string $format = null, + ): string { + $this->handler->setOutput($output = new BufferedOutput()); + $output->setVerbosity($this->verbosity); + + $this->handler->setInspector((new Inspector($exception))); + $this->handler->handle(); + + return $output->fetch(); + } + + public function canRender(string $format): bool + { + return $format === 'cli'; + } +} diff --git a/app/src/Exception/Handler.php b/app/src/Exception/Handler.php new file mode 100644 index 0000000..8607053 --- /dev/null +++ b/app/src/Exception/Handler.php @@ -0,0 +1,16 @@ +input->isJsonExpected() || $this->input->isXmlHttpRequest()) { + try { + return $core->callAction($controller, $action, $parameters); + } catch (ValidationException $e) { + return $this->responseWrapper->json(['errors' => $e->errors], 400); + } + } + + return $core->callAction($controller, $action, $parameters); + } +} diff --git a/app/src/Job/Ping.php b/app/src/Job/Ping.php index ccdde5f..a976a9a 100644 --- a/app/src/Job/Ping.php +++ b/app/src/Job/Ping.php @@ -5,17 +5,15 @@ namespace App\Job; use Spiral\Queue\JobHandler; -use Symfony\Contracts\HttpClient\HttpClientInterface; /** * (QueueInterface)->push(PingJob::class, ["value"=>"my value"]); */ class Ping extends JobHandler { - public function invoke(HttpClientInterface $client, string $url): void + public function invoke(array $payload): void { // do something - $status = $client->request('GET', $url)->getStatusCode() === 200; - echo $status ? 'PONG' : 'ERROR'; + echo $payload['value']; } } diff --git a/app/src/Repository/UserRepository.php b/app/src/Repository/UserRepository.php index ac8080b..0c149ad 100644 --- a/app/src/Repository/UserRepository.php +++ b/app/src/Repository/UserRepository.php @@ -1,12 +1,5 @@ findOne(['email' => $username]); } - /** - * @param TokenInterface $token - * @return object|null - */ - public function getActor(TokenInterface $token): ?object + public function getActor(TokenInterface $token): ?User { $data = $token->getPayload(); diff --git a/app/src/Request/Auth/LoginRequest.php b/app/src/Request/Auth/LoginRequest.php index 5708205..81a531a 100644 --- a/app/src/Request/Auth/LoginRequest.php +++ b/app/src/Request/Auth/LoginRequest.php @@ -1,86 +1,66 @@ 'data:username', - 'password' => 'data:password', - 'code' => 'data:code', - 'remember' => 'data:remember', - ]; - - protected const VALIDATES = [ - 'username' => ['notEmpty', 'string'], - 'password' => ['notEmpty', 'string'], - 'remember' => ['boolean'], - 'code' => ['string'], - ]; - - protected const SETTERS = [ - 'username' => 'strval', - 'password' => 'strval', - 'code' => 'strval', - 'remember' => 'boolval', - ]; - /** * @see https://en.wikipedia.org/wiki/ISO_8601#Durations */ private const DEFAULT_DURATION = 'P1D'; private const REMEMBER_DURATION = 'P1M'; - /** - * @return string - */ - public function getUsername(): string - { - return (string) $this->getField('username'); - } + #[Post] + #[Setter('strval')] + public readonly string $username; - /** - * @return string - */ - public function getPassword(): string - { - return (string) $this->getField('password'); - } + #[Post] + #[Setter('strval')] + public readonly string $password; + + #[Post] + #[Setter('strval')] + private readonly string $code; + + #[Post] + #[Setter('boolval')] + public bool $remember = false; - /** - * @return string|null - */ public function getCode(): ?string { - if ($this->getField('code') === '') { + if ($this->code === '') { return null; } - return $this->getField('code'); + return $this->code; } - /** - * @return \DateTimeInterface - */ public function getSessionExpiration(): \DateTimeInterface { $now = new \DateTime(); - if ((bool) $this->getField('rememberMe')) { + if ($this->remember) { return $now->add(new \DateInterval(self::REMEMBER_DURATION)); } return $now->add(new \DateInterval(self::DEFAULT_DURATION)); } + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition([ + 'username' => ['string', 'required'], + 'password' => ['string', 'required'], + 'code' => ['string'] + ]); + } } diff --git a/app/src/Request/Auth/LogoutRequest.php b/app/src/Request/Auth/LogoutRequest.php index 638aa77..638ce80 100644 --- a/app/src/Request/Auth/LogoutRequest.php +++ b/app/src/Request/Auth/LogoutRequest.php @@ -1,37 +1,26 @@ 'query:token', - ]; - - protected const VALIDATES = [ - 'token' => ['notEmpty', 'string'], - ]; - - protected const SETTERS = [ - 'token' => 'strval', - ]; + #[Input] + #[Setter('strval')] + public readonly string $token; - /** - * @return string - */ - public function getToken(): string + public function filterDefinition(): FilterDefinitionInterface { - return (string) $this->getField('token'); + return new FilterDefinition([ + 'token' => ['string', 'required'] + ]); } } diff --git a/app/src/Request/Keeper/Profile/UpdateRequest.php b/app/src/Request/Keeper/Profile/UpdateRequest.php index 9df0d21..5207f65 100644 --- a/app/src/Request/Keeper/Profile/UpdateRequest.php +++ b/app/src/Request/Keeper/Profile/UpdateRequest.php @@ -1,72 +1,43 @@ 'data:firstName', - 'lastName' => 'data:lastName', - 'email' => 'data:email', - 'password' => 'data:password', - 'confirmPassword' => 'data:confirmPassword', - 'currentPassword' => 'data:currentPassword', - ]; + #[Post] + #[Setter('strval')] + public readonly string $firstName; - protected const VALIDATES = [ - 'firstName' => ['notEmpty', 'string'], - 'lastName' => ['notEmpty', 'string'], - 'email' => [ - ['notEmpty'], - ['string'], - ['email'], - ['entity:unique', 'user', 'email', 'error' => 'Email address already used.'], - ], - 'password' => [ - ['string'], - [ - [PasswordHasher::class, 'checkPassword'], - 'error' => 'Password is too weak.', - 'if' => ['withAll' => ['password']], - ], - ], - 'confirmPassword' => [ - 'string', - ['notEmpty', 'if' => ['withAll' => ['password']]], - ['match', 'password', 'error' => 'Passwords do not match.'], - ], - 'currentPassword' => ['notEmpty', 'string'], - ]; + #[Post] + #[Setter('strval')] + public readonly string $lastName; + + #[Post] + #[Setter('strval')] + public readonly string $email; + + #[Post] + #[Setter('strval')] + public readonly string $password; + + #[Post] + #[Setter('strval')] + public readonly string $confirmPassword; - protected const SETTERS = [ - 'firstName' => 'strval', - 'lastName' => 'strval', - 'email' => 'strval', - 'password' => 'strval', - 'confirmPassword' => 'strval', - 'currentPassword' => 'strval', - ]; + #[Post] + #[Setter('strval')] + public readonly string $currentPassword; /** * @param User $user @@ -85,4 +56,31 @@ public function map(User $user, PasswordHasher $passwordManager): User return $user; } + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition([ + 'firstName' => ['string', 'required'], + 'lastName' => ['string', 'required'], + 'email' => [ + 'string', + 'required', + 'email', + ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]']], + 'password' => [ + 'string', + [ + [PasswordHasher::class, 'checkPassword'], + 'error' => '[[Password is too weak.]]', + 'if' => ['withAll' => ['password']], + ], + ], + 'confirmPassword' => [ + 'string', + ['required', 'if' => ['withAll' => ['password']]], + ['match', 'password', 'error' => '[[Passwords do not match.]]'], + ], + 'currentPassword' => ['string', 'required'] + ]); + } } diff --git a/app/src/Request/Keeper/User/CreateRequest.php b/app/src/Request/Keeper/User/CreateRequest.php index 8fab30d..92b28f1 100644 --- a/app/src/Request/Keeper/User/CreateRequest.php +++ b/app/src/Request/Keeper/User/CreateRequest.php @@ -1,72 +1,45 @@ 'data:firstName', - 'lastName' => 'data:lastName', - 'email' => 'data:email', - 'password' => 'data:password', - 'confirmPassword' => 'data:confirmPassword', - 'roles' => 'data:roles', - ]; + #[Post] + #[Setter('strval')] + public readonly string $firstName; + + #[Post] + #[Setter('strval')] + public readonly string $lastName; + + #[Post] + #[Setter('strval')] + public readonly string $email; + + #[Post] + #[Setter('strval')] + public readonly string $password; - protected const VALIDATES = [ - 'firstName' => ['notEmpty', 'string'], - 'lastName' => ['notEmpty', 'string'], - 'email' => [ - ['notEmpty'], - ['string'], - ['email'], - ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'], - ], - 'password' => [ - 'notEmpty', - 'string', - [[PasswordHasher::class, 'checkPassword'], 'error' => 'Password is too weak.'], - ], - 'confirmPassword' => [ - 'notEmpty', - 'string', - ['match', 'password', 'error' => 'Passwords do not match.'], - ], - 'roles' => [ - ['notEmpty', 'error' => 'At least one role is required.'], - ['array'], - [[RolesRequest::class, 'validRoles'], 'error' => 'Invalid roles.'], - ], - ]; + #[Post] + #[Setter('strval')] + public readonly string $confirmPassword; + + #[NestedFilter(class: RolesRequest::class)] + public readonly RolesRequest $roles; - /** - * @param User $user - * @param GuardInterface $guard - * @param PasswordHasher $passwords - * @return User - */ public function map(User $user, GuardInterface $guard, PasswordHasher $passwords): User { $user->firstName = $this->firstName; @@ -75,11 +48,35 @@ public function map(User $user, GuardInterface $guard, PasswordHasher $passwords $user->passwordHash = $passwords->hash($this->password); if ($guard->allows('users.roles', compact('user'))) { - $user->roles = join(',', $this->roles); + $user = $this->roles->map($user); } else { $user->roles = 'user'; } return $user; } + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition([ + 'firstName' => ['string', 'required'], + 'lastName' => ['string', 'required'], + 'email' => [ + 'string', + 'required', + 'email', + ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'] + ], + 'password' => [ + 'required', + 'string', + [[PasswordHasher::class, 'checkPassword'], 'error' => '[[Password is too weak.]]'], + ], + 'confirmPassword' => [ + 'string', + 'required', + ['match', 'password', 'error' => '[[Passwords do not match.]]'], + ], + ]); + } } diff --git a/app/src/Request/Keeper/User/RolesRequest.php b/app/src/Request/Keeper/User/RolesRequest.php index f136566..7125b8e 100644 --- a/app/src/Request/Keeper/User/RolesRequest.php +++ b/app/src/Request/Keeper/User/RolesRequest.php @@ -1,51 +1,29 @@ 'data:roles', - ]; - - protected const VALIDATES = [ - 'roles' => [ - ['notEmpty', 'error' => 'At least one role is required.'], - ['array'], - [[self::class, 'validRoles'], 'error' => 'Invalid roles.'], - ], - ]; - - /** - * @param User $user - * @return User - */ + #[Post] + public readonly array $roles; + public function map(User $user): User { - $user->roles = join(',', $this->roles); + $user->roles = \implode(',', $this->roles); + return $user; } - /** - * @param array $roles - * @return bool - */ public static function validRoles(array $roles): bool { foreach ($roles as $role) { @@ -56,4 +34,15 @@ public static function validRoles(array $roles): bool return true; } + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition([ + 'roles' => [ + 'array', + ['required', 'error' => '[[At least one role is required.]]'], + [[self::class, 'validRoles'], 'error' => '[[Invalid roles.]]'] + ] + ]); + } } diff --git a/app/src/Request/Keeper/User/UpdatePasswordRequest.php b/app/src/Request/Keeper/User/UpdatePasswordRequest.php index 0e38df5..ef9628c 100644 --- a/app/src/Request/Keeper/User/UpdatePasswordRequest.php +++ b/app/src/Request/Keeper/User/UpdatePasswordRequest.php @@ -1,53 +1,48 @@ 'data:password', - 'confirmPassword' => 'data:confirmPassword', - ]; - - protected const VALIDATES = [ - 'password' => [ - 'notEmpty', - 'string', - [[PasswordHasher::class, 'checkPassword'], 'error' => 'Password is too weak.'], - ], - 'confirmPassword' => [ - 'notEmpty', - 'string', - ['match', 'password', 'error' => 'Passwords do not match.'], - ], - ]; - - /** - * @param User $user - * @param PasswordHasher $passwordManager - * @return User - */ + #[Post] + #[Setter('strval')] + public readonly string $password; + + #[Post] + #[Setter('strval')] + public readonly string $confirmPassword; + public function map(User $user, PasswordHasher $passwordManager): User { $user->passwordHash = $passwordManager->hash($this->password); return $user; } + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition([ + 'password' => [ + 'required', + 'string', + [[PasswordHasher::class, 'checkPassword'], 'error' => '[[Password is too weak.]]'], + ], + 'confirmPassword' => [ + 'string', + 'required', + ['match', 'password', 'error' => '[[Passwords do not match.]]'], + ], + ]); + } } diff --git a/app/src/Request/Keeper/User/UpdateRequest.php b/app/src/Request/Keeper/User/UpdateRequest.php index a1f4a6b..8419eb3 100644 --- a/app/src/Request/Keeper/User/UpdateRequest.php +++ b/app/src/Request/Keeper/User/UpdateRequest.php @@ -1,47 +1,31 @@ 'data:email', - 'firstName' => 'data:firstName', - 'lastName' => 'data:lastName', - ]; - - protected const VALIDATES = [ - 'email' => [ - ['notEmpty'], - ['string'], - ['email'], - ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'], - ], - 'firstName' => ['notEmpty', 'string'], - 'lastName' => ['notEmpty', 'string'], - ]; - - /** - * @param User $user - * @return User - */ + #[Post] + #[Setter('strval')] + public readonly string $email; + + #[Post] + #[Setter('strval')] + public readonly string $firstName; + + #[Post] + #[Setter('strval')] + public readonly string $lastName; + public function map(User $user): User { $user->firstName = $this->firstName; @@ -50,4 +34,18 @@ public function map(User $user): User return $user; } + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition([ + 'email' => [ + 'string', + 'required', + 'email', + ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'] + ], + 'firstName' => ['string', 'required'], + 'lastName' => ['string', 'required'] + ]); + } } diff --git a/app/src/Security/PasswordHasher.php b/app/src/Security/PasswordHasher.php index 850a156..e232d8c 100644 --- a/app/src/Security/PasswordHasher.php +++ b/app/src/Security/PasswordHasher.php @@ -1,12 +1,5 @@ getRoles(); - if (in_array('admin', $roles, true) || in_array('super-admin', $roles, true)) { - return false; - } - - return true; + return !(\in_array('admin', $roles, true) || \in_array('super-admin', $roles, true)); } } diff --git a/app/src/Security/Rule/NotSelfRule.php b/app/src/Security/Rule/NotSelfRule.php index b432588..4f89c87 100644 --- a/app/src/Security/Rule/NotSelfRule.php +++ b/app/src/Security/Rule/NotSelfRule.php @@ -1,12 +1,5 @@ addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); + + // Set new handler and store previous one + $prevent = VarDumper::setHandler(static fn ($value) => $dumper->dump($cloner->cloneVar($value))); + $result = VarDumper::dump($value); + // Reset handler + VarDumper::setHandler($prevent); + + return $result; + } +} diff --git a/app/views/exception/403.dark.php b/app/views/exception/403.dark.php new file mode 100644 index 0000000..e009779 --- /dev/null +++ b/app/views/exception/403.dark.php @@ -0,0 +1,21 @@ + + + + + + + + +
+
+ Error 403 +

[[Access Forbidden]]

+ + + +
+ [[This view file is located in]] app/views/exception/403.dark.php. +
+
+
+
diff --git a/app/views/exception/404.dark.php b/app/views/exception/404.dark.php new file mode 100644 index 0000000..0a7a5db --- /dev/null +++ b/app/views/exception/404.dark.php @@ -0,0 +1,21 @@ + + + + + + + + +
+
+ Framework Logotype +

[[Page not found]]

+ + + +
+ [[This view file is located in]] app/views/exception/404.dark.php. +
+
+
+
diff --git a/app/views/exception/500.dark.php b/app/views/exception/500.dark.php new file mode 100644 index 0000000..b5d13bf --- /dev/null +++ b/app/views/exception/500.dark.php @@ -0,0 +1,21 @@ + + + + + + + + +
+
+ Error 500 +

[[Something went wrong]]

+ + + +
+ [[This view file is located in]] app/views/exception/500.dark.php. +
+
+
+
diff --git a/app/views/exception/error.dark.php b/app/views/exception/error.dark.php new file mode 100644 index 0000000..bfd9e44 --- /dev/null +++ b/app/views/exception/error.dark.php @@ -0,0 +1,21 @@ + + + + + + + + +
+
+ Framework Logotype +

[[Something went wrong]]. [[Error code]]: {{ $code }}

+ + + +
+ [[This view file is located in]] app/views/exception/error.dark.php. +
+
+
+
diff --git a/app/views/keeper/login.dark.php b/app/views/keeper/login.dark.php index 9a4e034..e852b26 100644 --- a/app/views/keeper/login.dark.php +++ b/app/views/keeper/login.dark.php @@ -36,7 +36,7 @@ - + diff --git a/composer.json b/composer.json index 1f49a53..aced5e3 100644 --- a/composer.json +++ b/composer.json @@ -15,21 +15,27 @@ } ], "require": { - "php": ">=8.0", + "php": ">=8.1", + "ext-mbstring": "*", "ext-json": "*", + "doctrine/collections": "^1.7", "fakerphp/faker": "^1.19", - "spiral/cycle-bridge": "^1.0", - "spiral/framework": "^2.9", + "spiral/cycle-bridge": "^2.0", + "spiral/framework": "^3.0", "spiral/helpers": "^1.0", - "spiral/keeper": "^0.9", - "spiral/nyholm-bridge": "^1.0", - "spiral/roadrunner-bridge": "^1.0", - "spiral/toolkit": "^v1.1", - "spiral/writeaway": "^0.4" + "spiral/keeper": "0.10", + "spiral/nyholm-bridge": "^1.2", + "spiral/roadrunner-bridge": "^2.0", + "spiral/sapi-bridge": "^1.0", + "spiral/toolkit": "^2.0", + "spiral/writeaway": "^0.5" }, "require-dev": { + "nunomaduro/collision": "^6.2", + "symfony/var-dumper": "^6.0", + "spiral-packages/ignition-bridge": "^1.0", "roave/security-advisories": "dev-master", - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.5.24" }, "scripts": { "post-create-project-cmd": [ @@ -42,7 +48,10 @@ "autoload": { "psr-4": { "App\\": "app/src" - } + }, + "files": [ + "app/src/helpers.php" + ] }, "autoload-dev": { "psr-4": { @@ -54,7 +63,10 @@ }, "config": { "sort-packages": true, - "bin-dir": "bin" + "bin-dir": "bin", + "allow-plugins": { + "spiral/composer-publish-plugin": true + } }, "minimum-stability": "dev", "prefer-stable": true From fc79af5616cee4f314127a27134cdc215f80fb88 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Mon, 19 Sep 2022 11:34:41 +0300 Subject: [PATCH 2/9] Fix CS --- app/src/Bootloader/AppBootloader.php | 4 ++-- app/src/Request/Auth/LoginRequest.php | 10 +++++----- app/src/Request/Auth/LogoutRequest.php | 2 +- app/src/Request/Keeper/Profile/UpdateRequest.php | 2 +- app/src/Request/Keeper/User/CreateRequest.php | 2 +- app/src/Request/Keeper/User/RolesRequest.php | 2 +- app/src/Request/Keeper/User/UpdateRequest.php | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/src/Bootloader/AppBootloader.php b/app/src/Bootloader/AppBootloader.php index 65c65a1..24e9e4d 100644 --- a/app/src/Bootloader/AppBootloader.php +++ b/app/src/Bootloader/AppBootloader.php @@ -13,7 +13,7 @@ class AppBootloader extends DomainBootloader { protected const SINGLETONS = [ - CoreInterface::class => [self::class, 'domainCore'] + CoreInterface::class => [self::class, 'domainCore'], ]; protected const BINDINGS = [ @@ -21,6 +21,6 @@ class AppBootloader extends DomainBootloader ]; protected const INTERCEPTORS = [ - ValidationInterceptor::class + ValidationInterceptor::class, ]; } diff --git a/app/src/Request/Auth/LoginRequest.php b/app/src/Request/Auth/LoginRequest.php index 81a531a..bf81aa3 100644 --- a/app/src/Request/Auth/LoginRequest.php +++ b/app/src/Request/Auth/LoginRequest.php @@ -27,14 +27,14 @@ class LoginRequest extends Filter implements HasFilterDefinition #[Setter('strval')] public readonly string $password; - #[Post] - #[Setter('strval')] - private readonly string $code; - #[Post] #[Setter('boolval')] public bool $remember = false; + #[Post] + #[Setter('strval')] + private readonly string $code; + public function getCode(): ?string { if ($this->code === '') { @@ -60,7 +60,7 @@ public function filterDefinition(): FilterDefinitionInterface return new FilterDefinition([ 'username' => ['string', 'required'], 'password' => ['string', 'required'], - 'code' => ['string'] + 'code' => ['string'], ]); } } diff --git a/app/src/Request/Auth/LogoutRequest.php b/app/src/Request/Auth/LogoutRequest.php index 638ce80..38922b2 100644 --- a/app/src/Request/Auth/LogoutRequest.php +++ b/app/src/Request/Auth/LogoutRequest.php @@ -20,7 +20,7 @@ class LogoutRequest extends Filter implements HasFilterDefinition public function filterDefinition(): FilterDefinitionInterface { return new FilterDefinition([ - 'token' => ['string', 'required'] + 'token' => ['string', 'required'], ]); } } diff --git a/app/src/Request/Keeper/Profile/UpdateRequest.php b/app/src/Request/Keeper/Profile/UpdateRequest.php index 5207f65..e8c3a64 100644 --- a/app/src/Request/Keeper/Profile/UpdateRequest.php +++ b/app/src/Request/Keeper/Profile/UpdateRequest.php @@ -80,7 +80,7 @@ public function filterDefinition(): FilterDefinitionInterface ['required', 'if' => ['withAll' => ['password']]], ['match', 'password', 'error' => '[[Passwords do not match.]]'], ], - 'currentPassword' => ['string', 'required'] + 'currentPassword' => ['string', 'required'], ]); } } diff --git a/app/src/Request/Keeper/User/CreateRequest.php b/app/src/Request/Keeper/User/CreateRequest.php index 92b28f1..fec632e 100644 --- a/app/src/Request/Keeper/User/CreateRequest.php +++ b/app/src/Request/Keeper/User/CreateRequest.php @@ -65,7 +65,7 @@ public function filterDefinition(): FilterDefinitionInterface 'string', 'required', 'email', - ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'] + ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'], ], 'password' => [ 'required', diff --git a/app/src/Request/Keeper/User/RolesRequest.php b/app/src/Request/Keeper/User/RolesRequest.php index 7125b8e..38707b8 100644 --- a/app/src/Request/Keeper/User/RolesRequest.php +++ b/app/src/Request/Keeper/User/RolesRequest.php @@ -41,7 +41,7 @@ public function filterDefinition(): FilterDefinitionInterface 'roles' => [ 'array', ['required', 'error' => '[[At least one role is required.]]'], - [[self::class, 'validRoles'], 'error' => '[[Invalid roles.]]'] + [[self::class, 'validRoles'], 'error' => '[[Invalid roles.]]'], ] ]); } diff --git a/app/src/Request/Keeper/User/UpdateRequest.php b/app/src/Request/Keeper/User/UpdateRequest.php index 8419eb3..31e09ec 100644 --- a/app/src/Request/Keeper/User/UpdateRequest.php +++ b/app/src/Request/Keeper/User/UpdateRequest.php @@ -42,10 +42,10 @@ public function filterDefinition(): FilterDefinitionInterface 'string', 'required', 'email', - ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'] + ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'], ], 'firstName' => ['string', 'required'], - 'lastName' => ['string', 'required'] + 'lastName' => ['string', 'required'], ]); } } From a3ba14caf78685ad74bdfc757cc67526939d1faf Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Mon, 19 Sep 2022 11:36:51 +0300 Subject: [PATCH 3/9] Fix CS --- app/src/Request/Keeper/Profile/UpdateRequest.php | 3 ++- app/src/Request/Keeper/User/RolesRequest.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/Request/Keeper/Profile/UpdateRequest.php b/app/src/Request/Keeper/Profile/UpdateRequest.php index e8c3a64..14ddbb6 100644 --- a/app/src/Request/Keeper/Profile/UpdateRequest.php +++ b/app/src/Request/Keeper/Profile/UpdateRequest.php @@ -66,7 +66,8 @@ public function filterDefinition(): FilterDefinitionInterface 'string', 'required', 'email', - ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]']], + ['entity:unique', 'user', 'email', 'error' => '[[Email address already used.]]'], + ], 'password' => [ 'string', [ diff --git a/app/src/Request/Keeper/User/RolesRequest.php b/app/src/Request/Keeper/User/RolesRequest.php index 38707b8..7bd6cb0 100644 --- a/app/src/Request/Keeper/User/RolesRequest.php +++ b/app/src/Request/Keeper/User/RolesRequest.php @@ -42,7 +42,7 @@ public function filterDefinition(): FilterDefinitionInterface 'array', ['required', 'error' => '[[At least one role is required.]]'], [[self::class, 'validRoles'], 'error' => '[[Invalid roles.]]'], - ] + ], ]); } } From 1706415980785e222d88eac1bb5f8f525bf16766 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Mon, 3 Oct 2022 12:46:07 +0300 Subject: [PATCH 4/9] Fix spiral/keeper version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index aced5e3..af49c80 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "spiral/cycle-bridge": "^2.0", "spiral/framework": "^3.0", "spiral/helpers": "^1.0", - "spiral/keeper": "0.10", + "spiral/keeper": "^0.10", "spiral/nyholm-bridge": "^1.2", "spiral/roadrunner-bridge": "^2.0", "spiral/sapi-bridge": "^1.0", From 1c60c688973eb83bb1c37522b9931179f45ce344 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Wed, 7 Dec 2022 19:40:53 +0200 Subject: [PATCH 5/9] Fix LoginMiddleware --- app/src/App.php | 6 ++++-- app/src/Bootloader/AdminBootloader.php | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/App.php b/app/src/App.php index 7e55ef0..87ab93f 100644 --- a/app/src/App.php +++ b/app/src/App.php @@ -120,10 +120,12 @@ class App extends Kernel // App Bootloader\RoutesBootloader::class, Bootloader\AppBootloader::class, - ]; - protected const APP = [ // fast code prototyping Prototype\PrototypeBootloader::class, ]; + + protected const APP = [ + // ... + ]; } diff --git a/app/src/Bootloader/AdminBootloader.php b/app/src/Bootloader/AdminBootloader.php index 7dfe263..bc2405c 100644 --- a/app/src/Bootloader/AdminBootloader.php +++ b/app/src/Bootloader/AdminBootloader.php @@ -6,6 +6,7 @@ use App\Controller\Keeper\DashboardController; use App\Interceptor\ValidationInterceptor; +use Spiral\Core\Container\Autowire; use Spiral\DataGrid\Interceptor\GridInterceptor; use Spiral\Cycle\Interceptor\CycleInterceptor; use Spiral\Domain\GuardInterceptor; @@ -30,7 +31,10 @@ class AdminBootloader extends KeeperBootloader GridInterceptor::class, ]; - protected const MIDDLEWARE = [ - LoginMiddleware::class, - ]; + protected function getMiddleware(): array + { + return [ + new Autowire(LoginMiddleware::class, ['loginView' => 'keeper:login']) + ]; + } } From d21ac9eb4a20aa4ab8f3ff77bb7e1c0e1bb1767c Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Wed, 7 Dec 2022 17:41:41 +0000 Subject: [PATCH 6/9] Apply fixes from StyleCI --- app/src/Bootloader/AdminBootloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/Bootloader/AdminBootloader.php b/app/src/Bootloader/AdminBootloader.php index bc2405c..7896422 100644 --- a/app/src/Bootloader/AdminBootloader.php +++ b/app/src/Bootloader/AdminBootloader.php @@ -34,7 +34,7 @@ class AdminBootloader extends KeeperBootloader protected function getMiddleware(): array { return [ - new Autowire(LoginMiddleware::class, ['loginView' => 'keeper:login']) + new Autowire(LoginMiddleware::class, ['loginView' => 'keeper:login']), ]; } } From 065fbd8c45f286625b63646feabbb7fc9f36171b Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Wed, 28 Feb 2024 10:20:38 +0200 Subject: [PATCH 7/9] Update dependencies --- .rr.yaml | 2 +- composer.json | 4 ++-- docker/php/Dockerfile | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.rr.yaml b/.rr.yaml index 5c79e05..f8957fe 100644 --- a/.rr.yaml +++ b/.rr.yaml @@ -1,4 +1,4 @@ -version: '2.7' +version: '3' rpc: listen: tcp://127.0.0.1:6001 diff --git a/composer.json b/composer.json index af49c80..acbf8a5 100644 --- a/composer.json +++ b/composer.json @@ -21,11 +21,11 @@ "doctrine/collections": "^1.7", "fakerphp/faker": "^1.19", "spiral/cycle-bridge": "^2.0", - "spiral/framework": "^3.0", + "spiral/framework": "^3.4", "spiral/helpers": "^1.0", "spiral/keeper": "^0.10", "spiral/nyholm-bridge": "^1.2", - "spiral/roadrunner-bridge": "^2.0", + "spiral/roadrunner-bridge": "^3.3", "spiral/sapi-bridge": "^1.0", "spiral/toolkit": "^2.0", "spiral/writeaway": "^0.5" diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 297b625..7803e6c 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -1,5 +1,5 @@ ARG PHP_IMAGE=8.1.3-cli-alpine3.15 -ARG ROAD_RUNNER_IMAGE=2.8.2 +ARG ROAD_RUNNER_IMAGE=2023.3.11 #Download rr binary FROM spiralscout/roadrunner:$ROAD_RUNNER_IMAGE as builder From d459822aa60bb21bef208c30a01172d3e5d60c36 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Wed, 28 Feb 2024 10:21:49 +0200 Subject: [PATCH 8/9] Fix StyleCI config --- .styleci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.styleci.yml b/.styleci.yml index 51f6e77..5bb1718 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -8,7 +8,6 @@ enabled: - linebreak_after_opening_tag - single_quote - no_blank_lines_after_phpdoc - - unary_operator_spaces - no_useless_else - no_useless_return - trailing_comma_in_multiline_array From d6ebf67f6c0f4d5b908c4d33522a0619442d9fa4 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 28 Nov 2024 13:57:40 +0400 Subject: [PATCH 9/9] set constrain for spiral/framework package --- composer.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index acbf8a5..b722604 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,12 @@ "email": "wolfy.jd@gmail.com" } ], + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/spiral" + } + ], "require": { "php": ">=8.1", "ext-mbstring": "*", @@ -21,7 +27,7 @@ "doctrine/collections": "^1.7", "fakerphp/faker": "^1.19", "spiral/cycle-bridge": "^2.0", - "spiral/framework": "^3.4", + "spiral/framework": "3.4 - 3.13", "spiral/helpers": "^1.0", "spiral/keeper": "^0.10", "spiral/nyholm-bridge": "^1.2",