Saba note

醜悪コード Ugly hacks ITものづくり

Laravelのメンテナンス画面のカスタマイズ

Laravel5は便利。メンテナンス中画面もすぐに作れるのでとても助かります。しかもやたらと安定しているし。

メンテナンス画面の作成

メンテナンスモードでメンテナンス画面を表示させるには、

resources/view/errors/503.blade.php

を作成して、503ステータス(Service Unavailable)で表示する画面を作成します。通常というか通例としてメンテナンス画面は503にしています。(これまでそうしてきました。)

メンテナンスモード実行

resources/views/errors/503.blade.php の内容を表示する

php artisan down

メンテナンスモード解除

php artisan up

指定したIPアドレスだけ普通にページを閲覧できるようにする

運用環境などで運用環境のみに依存するデバック作業なんかが発生したらページが閲覧できないと困りますが、妙なデバックコードをお客さんに見られても格好わるいので、許可したIPのみページ閲覧やデバックが可能な状態にしてその他のIPはすべてのページがメンテナンス画面になるようにします。

.envにパラメータ追加

カンマ区切りでIPアドレスを記述します。

MAINTENANCE_IP=192.0.0.10,172.16.1.10
ミドルウェアの作成

メンテナンスモードはミドルウェアでやっていて、デフォルトのクラスは、

vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php

におります。このクラスと同じことをしつつ機能を追加するとメンテナンスモードのカスタマイズができます。
例によってvendor以下は勝手に更新しないというのが常識なので、このクラスをオリジナルのミドルウェアとして作成します。

php artisan make:middleware CheckForMaintenanceMode

app/Http/Middleware/CheckForMaintenanceMode.phpが作成されます。中身は空っぽですがミドルウェアに最低限必要なコードが記述されています。
このクラスに先のデフォルトのクラスをマージします。

<?php

namespace App\Http\Middleware;

use Closure;
/* Required for IP restriction in maintenance mode */
use Illuminate\Contracts\Foundation\Application;
use Symfony\Component\HttpKernel\Exception\HttpException;

class CheckForMaintenanceMode
{
    protected $app;

    /**
     * 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
     */
    public function handle($request, Closure $next)
    {

       if ($this->app->isDownForMaintenance()) {
           $data = json_decode(file_get_contents($this->app->storagePath().'/framework/down'), true);

           throw new MaintenanceModeException($data['time'], $data['retry'], $data['message']);
       }

        return $next($request);
    }
}

ネームスペースとかタイプヒントとかはコードを見ながらマージしてください。

カーネルに登録
    protected $middleware = [
//        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, // IP許可なしのメンテ
        \App\Http\Middleware\CheckForMaintenanceMode::class, // IP許可ありのメンテ
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];

デフォルトのメンテナンスモードのミドルウェアをコメントアウトして新規で追加したものを追記します。
この時点で、デフォルトのメンテナンスモードの機能を保持しつつ機能追加する準備ができました。

最終的には、こんなかたちになりました。

<?php
namespace App\Http\Middleware;

use Closure;
/* Required for IP restriction in maintenance mode */
use Illuminate\Contracts\Foundation\Application;
use Symfony\Component\HttpKernel\Exception\HttpException;

/**
 * Original class
 * Illuminate\Foundation\Http\Middleware\CheckForMainenanceMode.php
 */
class CheckForMaintenanceMode
{
    protected $app;

    /**
     * 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
     */
    public function handle($request, Closure $next)
    {        
        if ($this->app->isDownForMaintenance()) {
             $ip = $request->getClientIp();
            $allowIp = explode(',', env('MAINTENANCE_IP'));
            if (!is_array($allowIp) || !in_array($ip, $allowIp)) {
                throw new HttpException(503);
            }
        }

//        if ($this->app->isDownForMaintenance()) {
//            $data = json_decode(file_get_contents($this->app->storagePath().'/framework/down'), true);
//
//            throw new MaintenanceModeException($data['time'], $data['retry'], $data['message']);
//        }

        return $next($request);
    }
}

おまけ

LB配下とかプロキシとか通した場合とかは、クライアントIPの取得の仕方が多少ややこしいですが、こんなような感じで動作します。
.envに「APP_ENV_LB=true」を設定しています。スタンドアローンとかのWEBサーバーだったら「APP_ENV_LB=false」でよいかと思います。

<?php

namespace App\Http\Middleware;

use Closure;
/* Required for IP restriction in maintenance mode */
use Illuminate\Contracts\Foundation\Application;
use Symfony\Component\HttpKernel\Exception\HttpException;

/**
 * Original class
 * Illuminate\Foundation\Http\Middleware\CheckForMainenanceMode.php
 * - Override above to add functionality
 */
class CheckForMaintenanceMode
{
    protected $app;

    /**
     * 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
     */
    public function handle($request, Closure $next)
    {
        if ($this->app->isDownForMaintenance()) {

            if (env('APP_ENV_LB')==true) {
                $requestHeaders = getallheaders();
                if (isset($requestHeaders["X-Cluster-Client-Ip"])) {
                    $ip = $requestHeaders["X-Cluster-Client-Ip"];
                } else {
                    $ip = $_SERVER["REMOTE_ADDR"];
                }
            } else {
                $ip = $request->getClientIp();
            }

            $allowIp = explode(',', env('MAINTENANCE_IP'));
            if (!is_array($allowIp) || !in_array($ip, $allowIp)) {
                throw new HttpException(503);
            }
        }

//        if ($this->app->isDownForMaintenance()) {
//            $data = json_decode(file_get_contents($this->app->storagePath().'/framework/down'), true);
//
//            throw new MaintenanceModeException($data['time'], $data['retry'], $data['message']);
//        }

        return $next($request);
    }
}

Laravel5でメンテナンス画面を実装する
Laravel 5 メンテナンスモード IP制限
メンテナンス画面の裏口

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です