diff --git a/framework/core/src/Api/Client.php b/framework/core/src/Api/Client.php index c1a6dbda4..77b747e4b 100644 --- a/framework/core/src/Api/Client.php +++ b/framework/core/src/Api/Client.php @@ -128,11 +128,27 @@ class Client $request = RequestUtil::withActor($request, $this->actor); } + $originalRequest = $this->container->make('request'); + return (new Pipeline($this->container)) ->send($request) - ->through($this->middlewareStack) - ->then(function (Request $request) { - return $this->container->make(Router::class)->dispatch($request); + ->then(function (Request $request) use ($originalRequest) { + $this->container->instance('request', $request); + + /** @var Router $router */ + $router = $this->container->make(Router::class); + + $originalMiddlewareGroup = $router->getMiddlewareGroups()['api']; + + $router->middlewareGroup('api', $this->middlewareStack); + + $response = $router->dispatch($request); + + $router->middlewareGroup('api', $originalMiddlewareGroup); + + $this->container->instance('request', $originalRequest); + + return $response; }); } } diff --git a/framework/core/src/Api/Controller/UpdateDiscussionController.php b/framework/core/src/Api/Controller/UpdateDiscussionController.php index ca9f18524..a9fa910c1 100644 --- a/framework/core/src/Api/Controller/UpdateDiscussionController.php +++ b/framework/core/src/Api/Controller/UpdateDiscussionController.php @@ -34,7 +34,7 @@ class UpdateDiscussionController extends AbstractShowController { $actor = RequestUtil::getActor($request); $discussionId = (int) $request->route('id'); - $data = $request->json()->all(); + $data = $request->json('data', []); /** @var Discussion $discussion */ $discussion = $this->bus->dispatch( diff --git a/framework/core/src/Api/Controller/UpdateGroupController.php b/framework/core/src/Api/Controller/UpdateGroupController.php index 8229e611a..3e639a23a 100644 --- a/framework/core/src/Api/Controller/UpdateGroupController.php +++ b/framework/core/src/Api/Controller/UpdateGroupController.php @@ -31,7 +31,7 @@ class UpdateGroupController extends AbstractShowController { $id = $request->route('id'); $actor = RequestUtil::getActor($request); - $data = $request->json()->all(); + $data = $request->json('data', []); return $this->bus->dispatch( new EditGroup($id, $actor, $data) diff --git a/framework/core/src/Api/Controller/UpdatePostController.php b/framework/core/src/Api/Controller/UpdatePostController.php index 701224f9e..46973a981 100644 --- a/framework/core/src/Api/Controller/UpdatePostController.php +++ b/framework/core/src/Api/Controller/UpdatePostController.php @@ -35,7 +35,7 @@ class UpdatePostController extends AbstractShowController { $id = $request->route('id'); $actor = RequestUtil::getActor($request); - $data = $request->json()->all(); + $data = $request->json('data', []); $post = $this->bus->dispatch( new EditPost($id, $actor, $data) diff --git a/framework/core/src/Api/Controller/UpdateUserController.php b/framework/core/src/Api/Controller/UpdateUserController.php index d06ae526b..af990eadf 100644 --- a/framework/core/src/Api/Controller/UpdateUserController.php +++ b/framework/core/src/Api/Controller/UpdateUserController.php @@ -17,7 +17,6 @@ use Flarum\User\Exception\NotAuthenticatedException; use Flarum\User\User; use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Http\Request; -use Illuminate\Support\Arr; use Tobscure\JsonApi\Document; class UpdateUserController extends AbstractShowController @@ -35,7 +34,7 @@ class UpdateUserController extends AbstractShowController { $id = $request->route('id'); $actor = RequestUtil::getActor($request); - $data = $request->json()->all(); + $data = $request->json('data', []); if ($actor->id == $id) { $this->serializer = CurrentUserSerializer::class; @@ -44,7 +43,7 @@ class UpdateUserController extends AbstractShowController // Require the user's current password if they are attempting to change // their own email address. if (isset($data['attributes']['email']) && $actor->id == $id) { - $password = (string) Arr::get($data, 'meta.password'); + $password = (string) $request->json('meta.password'); if (! $actor->checkPassword($password)) { throw new NotAuthenticatedException; diff --git a/framework/core/src/Api/routes.php b/framework/core/src/Api/routes.php index dac4f8aaf..d58eaa19e 100644 --- a/framework/core/src/Api/routes.php +++ b/framework/core/src/Api/routes.php @@ -67,8 +67,7 @@ return function (Router $router, RouteHandlerFactory $factory) { // Get a single user $router ->get('/users/{id}', $factory->toController(Controller\ShowUserController::class)) - ->name('users.show') - ->whereNumber('id'); + ->name('users.show'); // Edit a user $router @@ -146,8 +145,7 @@ return function (Router $router, RouteHandlerFactory $factory) { // Show a single discussion $router ->get('/discussions/{id}', $factory->toController(Controller\ShowDiscussionController::class)) - ->name('discussions.show') - ->whereNumber('id'); + ->name('discussions.show'); // Edit a discussion $router diff --git a/framework/core/src/Forum/Controller/ConfirmEmailController.php b/framework/core/src/Forum/Controller/ConfirmEmailController.php index c866f5917..322373bcf 100644 --- a/framework/core/src/Forum/Controller/ConfirmEmailController.php +++ b/framework/core/src/Forum/Controller/ConfirmEmailController.php @@ -30,7 +30,7 @@ class ConfirmEmailController extends AbstractController public function __invoke(Request $request): ResponseInterface { - $token = $request->query('token'); + $token = $request->route('token'); $user = $this->bus->dispatch( new ConfirmEmail($token) diff --git a/framework/core/src/Forum/Controller/ConfirmEmailViewController.php b/framework/core/src/Forum/Controller/ConfirmEmailViewController.php index 70d92ccec..da3dc4798 100644 --- a/framework/core/src/Forum/Controller/ConfirmEmailViewController.php +++ b/framework/core/src/Forum/Controller/ConfirmEmailViewController.php @@ -24,7 +24,7 @@ class ConfirmEmailViewController extends AbstractHtmlController public function render(Request $request): View { - $token = $request->query('token'); + $token = $request->route('token'); EmailToken::validOrFail($token); diff --git a/framework/core/src/Forum/Controller/ResetPasswordController.php b/framework/core/src/Forum/Controller/ResetPasswordController.php index 13e58f294..32328bbb5 100644 --- a/framework/core/src/Forum/Controller/ResetPasswordController.php +++ b/framework/core/src/Forum/Controller/ResetPasswordController.php @@ -26,7 +26,7 @@ class ResetPasswordController extends AbstractHtmlController public function render(Request $request): View { - $token = $request->query('token'); + $token = $request->route('token'); $token = PasswordToken::findOrFail($token); diff --git a/framework/core/src/Forum/ForumServiceProvider.php b/framework/core/src/Forum/ForumServiceProvider.php index f53d7a819..3c5968109 100644 --- a/framework/core/src/Forum/ForumServiceProvider.php +++ b/framework/core/src/Forum/ForumServiceProvider.php @@ -179,7 +179,12 @@ class ForumServiceProvider extends AbstractServiceProvider { $defaultRoutePath = ltrim($settings->get('default_route', '/all'), '/'); /** @var \Illuminate\Routing\Route $route */ - $route = $router->getRoutes()->getRoutesByMethod()['GET'][$defaultRoutePath]; + $route = $router->getRoutes()->getRoutesByMethod()['GET'][$defaultRoutePath] ?? null; + + if (!$route) { + $route = $router->getRoutes()->getRoutesByMethod()['GET']['all']; + } + $router->get('/', Arr::except($route->getAction(), ['as']))->name('forum.default'); } } diff --git a/framework/core/src/Foundation/ErrorHandling/ExceptionHandler.php b/framework/core/src/Foundation/ErrorHandling/ExceptionHandler.php index 062861ad2..929137892 100644 --- a/framework/core/src/Foundation/ErrorHandling/ExceptionHandler.php +++ b/framework/core/src/Foundation/ErrorHandling/ExceptionHandler.php @@ -83,11 +83,13 @@ class ExceptionHandler implements ExceptionHandling protected function resolveFormatter(Request $request): HttpFormatter { + $isApiFrontend = explode('/', trim($request->path(), '/'))[0] === $this->config->path('api'); + return match (true) { $request->expectsJson(), - $request->routeIs('api.*') => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof JsonApiFormatter), - $this->config->inDebugMode() => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof WhoopsFormatter), - default => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof ViewFormatter), + $isApiFrontend => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof JsonApiFormatter), + $this->config->inDebugMode() => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof WhoopsFormatter), + default => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof ViewFormatter), }; } } diff --git a/framework/core/src/Foundation/ErrorServiceProvider.php b/framework/core/src/Foundation/ErrorServiceProvider.php index cacb29ead..9f7ce7e7b 100644 --- a/framework/core/src/Foundation/ErrorServiceProvider.php +++ b/framework/core/src/Foundation/ErrorServiceProvider.php @@ -16,6 +16,7 @@ use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Validation\ValidationException as IlluminateValidationException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Tobscure\JsonApi\Exception\InvalidParameterException; class ErrorServiceProvider extends AbstractServiceProvider @@ -61,6 +62,7 @@ class ErrorServiceProvider extends AbstractServiceProvider return [ InvalidParameterException::class => 'invalid_parameter', ModelNotFoundException::class => 'not_found', + NotFoundHttpException::class => 'not_found', ]; }); diff --git a/framework/core/src/Http/Middleware/CheckCsrfToken.php b/framework/core/src/Http/Middleware/CheckCsrfToken.php index 052a0877c..7452d4e5c 100644 --- a/framework/core/src/Http/Middleware/CheckCsrfToken.php +++ b/framework/core/src/Http/Middleware/CheckCsrfToken.php @@ -46,7 +46,9 @@ class CheckCsrfToken implements IlluminateMiddlewareInterface { $expected = (string) $request->attributes->get('session')->token(); - $provided = $request->json('csrfToken', $request->header('X-CSRF-Token')); + $provided = $request->isJson() + ? $request->json('csrfToken', $request->header('X-CSRF-Token')) + : $request->header('X-CSRF-Token'); if (! is_string($provided)) { return false; diff --git a/framework/core/src/Http/Middleware/InjectActorReference.php b/framework/core/src/Http/Middleware/InjectActorReference.php index 57c4e74c3..d97c6fb39 100644 --- a/framework/core/src/Http/Middleware/InjectActorReference.php +++ b/framework/core/src/Http/Middleware/InjectActorReference.php @@ -19,6 +19,7 @@ class InjectActorReference implements IlluminateMiddlewareInterface { public function handle(Request $request, Closure $next): Response { + if (isset($GLOBALS['testing'])) dump('i', $request); $request = RequestUtil::withActor($request, new Guest); return $next($request); diff --git a/framework/core/src/Http/Middleware/SetLocale.php b/framework/core/src/Http/Middleware/SetLocale.php index 68c9654f0..5636f2c82 100644 --- a/framework/core/src/Http/Middleware/SetLocale.php +++ b/framework/core/src/Http/Middleware/SetLocale.php @@ -24,6 +24,7 @@ class SetLocale implements IlluminateMiddlewareInterface public function handle(Request $request, Closure $next): Response { + if (isset($GLOBALS['testing'])) dump('sl', $request); $actor = RequestUtil::getActor($request); if ($actor->exists) { diff --git a/framework/core/src/Http/RoutingServiceProvider.php b/framework/core/src/Http/RoutingServiceProvider.php index f3caebe42..90b55b04b 100644 --- a/framework/core/src/Http/RoutingServiceProvider.php +++ b/framework/core/src/Http/RoutingServiceProvider.php @@ -3,8 +3,13 @@ namespace Flarum\Http; use Flarum\Foundation\Config; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\Container\Container; use Illuminate\Routing\RoutingServiceProvider as IlluminateRoutingServiceProvider; +use Laminas\Diactoros\Response; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; class RoutingServiceProvider extends IlluminateRoutingServiceProvider { @@ -43,4 +48,18 @@ class RoutingServiceProvider extends IlluminateRoutingServiceProvider return $url; }); } + + protected function registerPsrRequest(): void + { + $this->app->bind(ServerRequestInterface::class, function ($app) { + return RequestUtil::toPsr7($app->make('request')); + }); + } + + protected function registerPsrResponse(): void + { + $this->app->bind(ResponseInterface::class, function () { + return new Response(); + }); + } } diff --git a/framework/core/src/Http/Server.php b/framework/core/src/Http/Server.php index 0c0f928c9..9799c90dc 100644 --- a/framework/core/src/Http/Server.php +++ b/framework/core/src/Http/Server.php @@ -43,6 +43,8 @@ class Server ->send($request) ->through($globalMiddleware) ->then(function (Request $request) use ($app) { + $app->instance('request', $request); + return $app->make(Router::class)->dispatch($request); }); } diff --git a/framework/core/src/User/Throttler/EmailActivationThrottler.php b/framework/core/src/User/Throttler/EmailActivationThrottler.php index 767c3f32f..79592526d 100644 --- a/framework/core/src/User/Throttler/EmailActivationThrottler.php +++ b/framework/core/src/User/Throttler/EmailActivationThrottler.php @@ -24,7 +24,7 @@ class EmailActivationThrottler public function __invoke(Request $request): ?bool { - if ($request->routeIs('api.users.confirmation.send')) { + if (! $request->routeIs('api.users.confirmation.send')) { return null; } diff --git a/framework/core/src/User/Throttler/EmailChangeThrottler.php b/framework/core/src/User/Throttler/EmailChangeThrottler.php index 09d492b29..deecd5a5d 100644 --- a/framework/core/src/User/Throttler/EmailChangeThrottler.php +++ b/framework/core/src/User/Throttler/EmailChangeThrottler.php @@ -24,7 +24,7 @@ class EmailChangeThrottler public function __invoke(Request $request): ?bool { - if ($request->routeIs('api.users.update')) { + if (! $request->routeIs('api.users.update')) { return null; } diff --git a/framework/core/src/User/Throttler/PasswordResetThrottler.php b/framework/core/src/User/Throttler/PasswordResetThrottler.php index 5943fdad6..a04cc57da 100644 --- a/framework/core/src/User/Throttler/PasswordResetThrottler.php +++ b/framework/core/src/User/Throttler/PasswordResetThrottler.php @@ -25,7 +25,7 @@ class PasswordResetThrottler public function __invoke(Request $request): ?bool { - if ($request->routeIs('forum.forgot')) { + if (! $request->routeIs('forum.forgot')) { return null; }