Reworked maintenance system. Also had to rework a few HTTP error related things because the 404 page wasn't being run through middleware.

This commit is contained in:
Graphictoria 2022-04-21 22:37:18 -04:00
parent 4a06844a53
commit 0e4cec86cc
11 changed files with 185 additions and 209 deletions

View File

@ -50,12 +50,15 @@ class Handler extends ExceptionHandler
return response()->view('errors.403', [], 403);
});
/*
// Moved to route fallback
$this->renderable(function (NotFoundHttpException $e, $request) {
return response()->view('errors.404', [], 404);
});
// Moved to middleware
$this->renderable(function (\ErrorException $e, $request) {
return response()->view('errors.500', ['stack' => $e->getTraceAsString()], 500);
});
});*/
}
}

View File

@ -20,7 +20,7 @@ class Kernel extends HttpKernel
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\Illuminate\Session\Middleware\StartSession::class
\Illuminate\Session\Middleware\StartSession::class,
];
/**
@ -30,6 +30,7 @@ class Kernel extends HttpKernel
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\Exception\WebException::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
@ -40,21 +41,11 @@ class Kernel extends HttpKernel
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
],
'admin' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
],
];

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Middleware\Exception;
use Closure;
class WebException
{
public function handle($request, Closure $next)
{
$response = $next($request);
if ($response->exception) {
return response()->view('errors.500', ['stack' => $response->exception->getTraceAsString()], 500);
}
return $response;
}
}

View File

@ -3,139 +3,33 @@
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Route;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Http\MaintenanceModeBypassCookie;
use Symfony\Component\HttpKernel\Exception\HttpException;
class PreventRequestsDuringMaintenance
{
/**
* The application implementation.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* The URIs that should be accessible while maintenance mode is enabled.
*
* @var array
*/
protected $except = ['maintenance', 'maintenance/bypass'];
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
public function handle($request, Closure $next)
{
if($this->app->isDownForMaintenance()) {
$data = json_decode(file_get_contents($this->app->storagePath().'/framework/down'), true);
if ($this->hasValidBypassCookie($request, $data) ||
$this->inExceptArray($request)) {
return $next($request);
if(in_array('web', $request->route()->middleware()))
{
if($request->route()->uri() != 'maintenance')
return redirect('/maintenance?ReturnUrl=' . urlencode(url()->full()));
}
return response('{"errors":[{"code":503,"message":"ServiceUnavailable"}]}', 503)
else
{
return response(['errors' => [['code' => 503, 'message' => 'ServiceUnavailable']]], 503)
->header('Cache-Control', 'private')
->header('Content-Type', 'application/json; charset=utf-8');
}
}
return $next($request);
}
/**
* Determine if the incoming request has a maintenance mode bypass cookie.
*
* @param \Illuminate\Http\Request $request
* @param array $data
* @return bool
*/
protected function hasValidBypassCookie($request, array $data)
{
return isset($data['secret']) &&
$request->cookie('gt_constraint') &&
MaintenanceModeBypassCookie::isValid(
$request->cookie('gt_constraint'),
$data['secret']
);
}
/**
* Determine if the request has a URI that should be accessible in maintenance mode.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->fullUrlIs($except) || $request->is($except)) {
return true;
}
}
return false;
}
/**
* Redirect the user back to the root of the application with a maintenance mode bypass cookie.
*
* @param string $secret
* @return \Illuminate\Http\RedirectResponse
*/
protected function bypassResponse(string $secret)
{
return redirect('/')->withCookie(
MaintenanceModeBypassCookie::create($secret)
);
}
/**
* Get the headers that should be sent with the response.
*
* @param array $data
* @return array
*/
protected function getHeaders($data)
{
$headers = isset($data['retry']) ? ['Retry-After' => $data['retry']] : [];
if (isset($data['refresh'])) {
$headers['Refresh'] = $data['refresh'];
}
return $headers;
}
/**
* Get the URIs that should be accessible even when maintenance mode is enabled.
*
* @return array
*/
public function getExcludedPaths()
{
return $this->except;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Providers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class MaintenanceServiceProvider extends ServiceProvider
{
protected $app;
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Blade::directive('live', function() {
return '<?php if(!app()->isDownForMaintenance()): ?>';
});
Blade::directive('endlive', function() {
return '<?php endif; ?>';
});
}
}

View File

@ -37,7 +37,7 @@ class RouteServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->configureRateLimiting();
//$this->configureRateLimiting();
$this->routes(function () {
Route::domain('apis.' . env('APP_URL'))
@ -66,7 +66,7 @@ class RouteServiceProvider extends ServiceProvider
->group(base_path('routes/versioncompatibility.php'));
Route::domain('impulse.' . env('APP_URL'))
->middleware('admin')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/admin.php'));
@ -82,15 +82,15 @@ class RouteServiceProvider extends ServiceProvider
});
}
/**
* Configure the rate limiters for the application.
*
* @return void
*/
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
});
}
// /**
// * Configure the rate limiters for the application.
// *
// * @return void
// */
// protected function configureRateLimiting()
// {
// RateLimiter::for('api', function (Request $request) {
// return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
// });
// }
}

View File

@ -139,7 +139,7 @@ return [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
App\Providers\MaintenanceServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,

View File

@ -4,10 +4,13 @@
@section('content')
<div class="container graphictoria-center-vh">
@env(['staging', 'local'])
<br />
@endenv
<x-card title="INTERNAL SERVER ERROR">
<x-slot name="body">
Oops, we ran into an issue while trying to process your request, please try again later in a few minutes. If the issue persists after a few minutes, please contact us at <a href="mailto:support@gtoria.net" class="fw-bold text-decoration-none">support@gtoria.net</a>.
@env(['production', 'staging'])
@env(['staging', 'local'])
@if(isset($stack))
<div class="border border-primary bg-dark p-3 m-4">
<code>
@ -24,5 +27,8 @@
</div>
</x-slot>
</x-card>
@env(['staging', 'local'])
<br />
@endenv
</div>
@endsection

View File

@ -31,6 +31,7 @@
<div class="footer mt-auto pt-3 text-center shadow-lg">
<div class="container">
<h4 class="fw-bold mb-0">Graphictoria</h4>
@live
<p class="text-muted fw-bold mb-0 mt-1">
@foreach($routes as $index => $route)
@php
@ -43,6 +44,7 @@
@endif
@endforeach
</p>
@endlive
<hr class="mx-auto my-2 w-25"/>
<p class="text-muted fw-light m-0">Copyright © {{ \Carbon\Carbon::now()->format('Y') }} Graphictoria. All rights reserved.</p>
<p class="text-muted fw-light m-0">Graphictoria is not affiliated with, endorsed by, or sponsored by Roblox Corporation. The usage of this website signifies your acceptance of the <a class="text-decoration-none fw-normal" href="{{ url('/legal/terms-of-use') }}">Terms of Use</a> and our <a class="text-decoration-none fw-normal" href="{{ url('/legal/privacy-policy') }}">Privacy Policy</a>.</p>

View File

@ -18,14 +18,21 @@
<div class="navbar graphictoria-navbar fixed-top navbar-expand-md shadow-sm">
<div class="container-md">
@live
<a class="navbar-brand" href="/">
<img src="{{ asset('/images/logo.png') }}" alt="Graphictoria" width="43" height="43" draggable="false"/>
</a>
@else
<i class="navbar-brand">
<img src="{{ asset('/images/logo.png') }}" alt="Graphictoria" width="43" height="43" draggable="false"/>
</i>
@endlive
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#graphictoria-nav" aria-controls="graphictoria-nav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="graphictoria-nav">
<ul class="navbar-nav me-auto">
@live
@foreach($routes as $route)
@php
// HACK
@ -42,7 +49,13 @@
<li><a class="dropdown-item" href="https://discord.gg/q666a2sF6d" target="_blank" rel="noreferrer">Discord</a></li>
</ul>
</li>
@else
<li class="nav-item">
<a class="nav-link" href="https://discord.gg/q666a2sF6d" target="_blank" rel="noreferrer">Discord</a>
</li>
@endlive
</ul>
@live
@if($authenticated)
<div class="flex">
<p class="my-auto me-2 text-muted" style="color:#e59800!important;font-weight:bold">
@ -67,6 +80,7 @@
@else
<a class="btn btn-success" href="/login">Login / Sign up</a>
@endif
@endlive
</div>
</div>
</div>

View File

@ -22,3 +22,8 @@ Route::view('/javascript', 'javascript');
// client
Route::get('/asset', 'ContentController@fetchAsset');
// fallback
Route::fallback(function(){
return response()->view('errors.404', [], 404);
});