withRouting( web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', channels: __DIR__.'/../routes/channels.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { // The app only ever receives traffic from the Traefik reverse proxy on the // internal docker network (TLS is terminated there), so trust its // X-Forwarded-* headers — otherwise Laravel thinks every request is plain // HTTP and generates http:// asset/URL links, which browsers block as // mixed content on the https:// site. $middleware->trustProxies(at: '*', headers: Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB); $middleware->alias([ 'role' => EnsureRole::class, 'abilities' => CheckAbilities::class, 'ability' => CheckForAnyAbility::class, ]); // navigator.sendBeacon (used to stop the ticket timer on tab close, // see routes/web.php) can't attach a CSRF header/field. $middleware->validateCsrfTokens(except: [ 'operator/tickets/*/stop-timer', ]); }) ->withExceptions(function (Exceptions $exceptions): void { $exceptions->shouldRenderJsonWhen( fn (Request $request) => $request->is('api/*'), ); // A ticket deleted mid-session (typically by the operator/client // currently viewing it) leaves any later request for that same // {ticket} route binding 404ing — most commonly Livewire's own // "model missing during hydration" recovery, which does a full // window.location.reload() of the very page whose ticket just // disappeared (e.g. the ticket-show view's periodic fallback // refresh polling a few seconds after a delete+redirect). Land back // on that area's own list page instead of a raw 404. // // Handler::prepareException() already converts ModelNotFoundException // into NotFoundHttpException (wrapping the original as getPrevious()) // before any render() callback is dispatched — a callback typed // against ModelNotFoundException itself would simply never match. $exceptions->render(function (NotFoundHttpException $e, Request $request) { if (! $e->getPrevious() instanceof ModelNotFoundException || ! $request->user()) { return null; } return match (true) { $request->is('operator/*') => redirect()->route('operator.queue'), $request->is('client/*') => redirect()->route('client.dashboard'), default => null, }; }); })->create();