@jaheller You could write your own middleware based on CheckForMaintenanceMode and work with an exclusion list like the VerifyCsrfToken middleware. But keep in mind that you either need to exclude the login route or provide a special admin login route otherwise you won't be able to login.
In order to enable this middleware permanently put it into kernel.php's middleware array.
maintenance mode (excluding some routes)
I know laravel comes with a maintenance mode. I have my admin panel (routes start with /admin). Now I would like to have a button in my admin panel to set the whole laravel app into maintenance mode, so visitors should see a static HTML site but I would like to be able to work in the admin panel while app is in maintenance mode. -All hints/ideas welcome!
How can I achieve this best?
@skliche Admin login would be at /admin/login. Excluding /admin/* should do this. Could you tell me more about your idea (middleware, where to store the "status" of maintenance...). I tought the same, using the default maintenance mode would be to hard to modify.
@jaheller Base your middleware on the exact same code as CheckForMaintenanceMode and therefor utilize the same method (file /storage/framework/down). Replace the line \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, in kernel.php with your own middleware.
protected $except = [
// URIs, https://laravel.com/docs/master/routing#csrf-excluding-uris
'admin/*',
];
public function handle($request, Closure $next) {
if ($this->app->isDownForMaintenance() && ! $this->shouldPassThrough($request)) {
throw new HttpException(503);
}
return $next($request);
}
shouldPassThrough() would check the current route against the exempted routes. See shouldPassThrough() in /vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php. You should be able to use the exact same code.
The steps I understand how to do.
-Create before middleware in app/Http/Middleware directory (code above should do the job)
-Replace the line \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, in kernel.php
That's all? No need to "attach" middleware to each controller like this right? Because it runs without being attached to a controller?
public function __construct()
{
$this->middleware(['whatever_the_name_may_be_of_middleware?','auth']);
}
@jaheller Yes, as long as you replace the existing line in kernel.php with your middleware there is no need to call it in a controller or specify it on a route / route group as this is the global middleware stack. Therefor it will be called for every single request to your application.
Sorry for late answer, now I'm on this. Error saying
Undefined property: App\Http\Middleware\MaintenanceMiddleware::$app
It's the line with the if statement. Did I need to include something before? my MaintenanceMiddleware:
<?php
namespace App\Http\Middleware;
use Closure;
class MaintenanceMiddleware
{
protected $except =
[
// URIs, https://laravel.com/docs/master/routing#csrf-excluding-uris
'admin/*',
];
public function handle($request, Closure $next)
{
if ($this->app->isDownForMaintenance() && ! $this->shouldPassThrough($request))
{
throw new HttpException(503);
}
return $next($request);
}
}
i added a pull request for this, but it was disappointingly turned down, the implementation worked and passed all tests, take a look as it may help you here: https://github.com/laravel/laravel/pull/3589
Create before middleware in app/Http/Middleware directory and you replace the living line in kernel.php with your middleware there is no need to call it in a controller . That's all i know.
Works great. Sad to see such a great pull request was turned down. Specially working with payment processors like paypal this will be the best solution to be able to exclude IPs and routes at the same time. Sorry for you, but you've done great work. Works fine :). At least one happy user :/.
great to hear it, yes my argument was callbacks for payments and admin usage.
maybe it will get there some day, in the meantime its not too tiresome to implement.
@no12pixels Did you have any idea how I can easily turn the laravel app into maintenance mode and back to live by just using a button in my admin panel? So changing the status of the app without using "command line (artisan)"?
@jaheller should be pretty simple, just use the artisan facade:
Artisan::call('up')
performs the same task as console artisan commands, its in the docs here: https://laravel.com/docs/5.2/artisan#calling-commands-via-code
you would use this command inside a controller method by checking if the button is pressed when the forms submitted. eg:
public function postMode(Request $request){
if($request->input('enabled_maintenance')){
Artisan::call('down');
return ....;
}
if($request->input('disable_maintenance')){
Artisan::call('up');
return ....;
}
}
@lee__mason I'm following your example, but it doesn't seem to be getting anything can you give the parts you do about it
@DucTonyVN don't follow advice in this question. maintenance mode has been improved in the last 5 years
@Snapey The real problem here is that I have to create a button to click on maintenance I can't use php artisan down and up for maintenance
Today I noticed the exclude of routes seems to be not working. I use this to exclude route:
protected $excludedRoutes = ['admin/*' , '/stats'];
Using the exclude IP adresses works fine. my routes:
Route::get('admin', 'DashboardController@admin');
@jaheller its not so much thats its now working, more that its not designed to work with wildcards.
the $excludedRoutes property takes in an array of concrete route names, not wildcard route names, maybe something i wrap up in a package with additional functionality.
@no12pixels I tried to use:
protected $excludedRoutes = ['stats', 'admin'];
route:
//stats routes
Route::get('stats', 'statsController@index');
doesn't work too. Exclude IPs works fine. Where's the fault? //admin panel Route::get('admin', 'DashboardController@admin');
you need to give the route a name, its not a url matcher, its a route name matcher, your route should look like this:
Route::get('stats', ['as' => 'stats', 'uses' => 'statsController@index']);
@no12pixels This problem might be related to the except: https://laracasts.com/discuss/channels/laravel/exclude-admin-panel-as-folder
Accepting controller actions or wildcard URLs would be great ;D. Works now but using wildcard URLs or controller actions (ExampleController@index) would make it easier to edit routes.
I modified no12pixels code a bit to something like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Routing\Route;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Original;
class CheckForMaintenanceMode extends Original
{
protected $excludedNames = [];
protected $except = ['admin/*', 'stats'];
protected $excludedIPs = [];
protected function shouldPassThrough($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->is($except)) {
return true;
}
}
return false;
}
/**
* 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()) {
$response = $next($request);
if (in_array($request->ip(), $this->excludedIPs)) {
return $response;
}
$route = $request->route();
if ($route instanceof Route) {
if (in_array($route->getName(), $this->excludedNames)) {
return $response;
}
}
if ($this->shouldPassThrough($request))
{
dd("YA");
return $response;
}
throw new HttpException(503);
}
return $next($request);
}
}
Any idea why I get a maintenance page when I will /admin? When I use for example /admin/random it works fine. How to get it working by route "/admin" too?
@jaheller Your current code needs a trailing slash after admin, try protected $except = ['admin*', 'stats'];
@skliche Works, but is there an easy way to get the rule just match on "exact" route. For example:
/adminHAHSAH/ndsanndsa ->valid, but only /admin and /admin/herecomeroutes should be valid /admin/dhsah->valid correct!
At the moment it seems like it just checks if the route contains "admin", if this returns true the whole route is valid, that'why why something like "adminBLA/xyz" is valid too.
@jaheller $except = ['admin', 'admin/*', 'stats'];
How can you change the expluded ips?
hen can i put this array to access from anyside of my code like a global variable?
Please or to participate in this conversation.