「Linux」カテゴリーアーカイブ

Let’s EncryptのSSL証明書の有効期限が切れていました。(解決済み)

いやー焦った焦った。

ブログの様子を見ようと思ったらこれだよ。

ERR_CERT_DATE_INVALID

というメッセージから読み取るに、

エラー、証明書、日付、不正。

ああ、証明書の日付がおかしな事になっているのね。

このサイトのSSL証明書はLet’s Encryptというツール(?)で発行しているので

ただ、自動更新する設定になっているはずだが、それがたまたま上手くいっていないのだろう。

SSHでサーバにログインし、証明書の日付を確認する。

コマンドはこれ。

$ sudo openssl x509 -in /etc/letsencrypt/live/【ドメイン名】/fullchain.pem -noout -dates

notAfterが証明書の有効期限が切れる日付です。

なるほど、8月20日午前7時11分(標準時刻)で期限が切れていたみたい。

$ sudo letsencrypt renew

で、手動による証明書更新を試みたものの、なぜかスキップされたので、

強制的に更新させました。

$ sudo letsencrypt renew --force-renewal

11月19日午前11時32分(標準時刻)に更新されました。

あ、標準時刻だから、日本時間はこれに+9時間する必要があるからね。

そして、忘れてはいけない、nginxの再起動。

$ sudo systemctl restart nginx.service

エラーは解消されました!!

【ラズパイ】Raspberry PI4が届いた!

買いました。

Raspberry Pi 4 Model B 2GB RAM。

これの一個前がRaspberry Pi 2 Model Bだったから、それより大幅なパワーアップです。

メモリもプロセッサーも強化されていますが、わかりやすいところで言うと、

HDMIポート1個から、Micro HDMIのポート2つになりました。

4K出力もできるみたいよぉ。

Micro HDMI対応のケーブルなんてほとんど出回っていないので、本体と一緒に注文しました。

Micro HDMI – HDMIのケーブルです。

これが無いと、ディスプレイに画面を表示できません。

そして、もう一つ、左側にあるのが電源です。

端子はUSB-Cなのですが、これを動かすのに3Aの出力が必要なので、一般に出回っているUSB-Cケーブルでは動きません。

これも本体と同時に対応電源を注文しました。

あとは、ストレージとなるMicro SDカード。

これだけでこのラズパイは動かせるのですが、ついでなので、もうちょっと遊べるパーツを買いました。

このボードに温度計、湿度計などなど、あと右下にジョイスティック、8×8のLEDディスプレイが付いているパーツです。

ラズパイ4でも動くはず。

さあ、セットアップするぞ。

この一式はこちらのサイトで購入しました。

https://raspberry-pi.ksyic.com

【Laravel】【ホテル予約管理】解錠APIを追加する

前回までの状況はこちら。

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/hotel-mng

さて、前回からだいぶ期間が空いてしまいましたが、

続きやります。(思い出すのに時間かかった)

今回は、前回メールに添付したQRコードに書かれた解錠コードを使って解錠する処理を実装するのですが、

実際に解錠を行うのは、外部のデバイスを使用する、という前提として考え、ここではデバイスに入力した解錠コードをサーバに送信し、照合結果を返すAPIを実装します。

このAPIのレスポンスをデバイスが受信して、デバイスが実際に解錠を行います。そういう想定で実装します。

まず、APIのルーティングを作成します。

routes配下のapi.phpに以下を追加します。

Route::group(['middleware' => ['api']], function(){
    Route::post('/unlock', 'Api2Controller@unlock');
});

なぜ、今までのようにweb.phpではないかというと、web.phpには

Auth::routes();

がかかれてあり、ログイン状態でアクセスしなければAPIは受け付けられません。

デバイス側にログイン状態というのは意識できないので、この制約が入らないルートに設定しなければなりません。

では、コントローラーを作成します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Repository\RegisterManagementRepository;
use App\Repository\RoomRepository;
use App\Repository\UserRepository;

class Api2Controller extends Controller
{
    public function __construct()
    {
        $this->registerManagement = new RegisterManagementRepository();
        $this->room = new RoomRepository();
        $this->user = new UserRepository();
    }

    public function unlock(Request $request)
    {
        \Log::debug(print_r($request->input('number'), true));
        \Log::debug(print_r($request->input('room'), true));

        $registers = $this->registerManagement->getItemsByDate(date('Y-m-d'));
        if(is_null($registers) == false)
        {
            foreach ($registers as $register)
            {
                $room = $register->rooms()->first();
                if(is_null($room) == false)
                {
                    if($room->id == $request->input('room') &&
                        strcmp($register->lock_number, $request->input('number')) == 0)
                    {
                        return response()->json(['result' => true]);
                    }
                }
            }
        }
        return response()->json(['result' => false]);
    }
}

Api2Controller.phpを追加しました。

ApiController.phpでは、コンストラクタに

        $this->middleware('auth');

と記述しているため、これも上記と同じ理由で使用できません。

入力は部屋番号と解錠コードをjsonで入力します。

現在の日付から予約情報を取得し、部屋番号を解錠コードの比較を行い、一致したときにtrue、一致しない場合はfalseを返します。

思ったように動いているようです。

ただし、このままだと誰でもアクセスできてしまうので、セキュリティ的にガバガバ状態です。

デバイス情報を登録して、それも入力情報に含めるようにしましょうか。

次回やります。

【Laravel】まっさらな状態のLinuxから環境構築

この記事、定期的にアップデートしていこう。

MySQLのセットアップ

composerのインストール

$ sudo apt-get install composer

phpの足りないモジュールをインストール

$ sudo apt-get install php7.2-mbstring
$ sudo apt-get install php7.2-xml
$ sudo apt-get install php7.2-zip
$ sudo apt-get install php7.2-gd
$ sudo apt-get install php7.2-mysql

nginxのインストール

Laravelの設定

$ composer global require "laravel/installer"
$ cd /var/www
$ sudo chmod 777 html
$ cd html

リポジトリからLaravelのソースをclone

$ git clone リポジトリのURL
$ chmod 777 storage/
$ chmod 777 bootstrap/cache/

.envを作成して、設定を記入

$ composer install
$ php artisan key:generate

node.jsのセットアップ

$ sudo apt-get install npm
$ sudo apt-get install libpng-dev
$ npm install
$ npm run dev

データベースを設定

$ php artisan migrate
$ php artisan db:seed --class=実行するseederクラス

【Linux】mysqlをセットアップしたら躓いたので

忘れないようにメモしておく。

MySQLのインストール

$ sudo apt-get install mysql-server

rootパスワード設定

$ sudo mysql_secure_installation

問い合わせはすべてyを入力。

rootでログインを確認

$ sudo mysql -u root -p

MySQLにユーザーを追加

mysql> CREATE USER 'user'@'localhost' IDENTIFIED BY 'password'

ユーザーに権限を設定

mysql> GRANT all ON *.* TO 'user'@'localhost'

【LARAVEL】【ホテル予約管理】QRコードをメールに添付して送信する

前回までの状況はこちら

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/hotel-mng

さて、前回作成したQRコードをメールに添付して送信する実装を行います。

メーラーはGMAILを使用します。

自前のsmtpサーバを使おうとしたけど、うまくいかなかった。

smtpサーバの設定を.envに記入します。

MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=xxxxx@gmail.com
MAIL_PASSWORD=xxxxx
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=xxxxx@gmail.com
MAIL_FROM_NAME=xxxxxx

内容を使用しているアカウント情報に合わせてください。

tinkerを使って、設定が正しいかを確認することができます。

$ php artisan tinker
>>> Mail::raw('test mail',function($message) {$message->to('xxxxx@gmail.com')->subject('test');});
=> null

正しく設定されていれば、メールが届くはずです。

※エラーが表示される場合は、GMAIL側の設定の問題だったりします。セキュリティ設定を変えてみてください。

では、メールを扱う処理を書いていきましょう。

以下のコマンドでメールクラスを作成します。

$ php artisan make:mail SendQRcodeMail

以下のように書き換えます。

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendQRcodeMail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($img)
    {
        $this->img = $img;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('emails.mail')
            ->from('manager@gmail.com','manager')
            ->subject('予約しました')
            ->attach($this->img);
    }
}

コンストラクタでQRコードの画像パスを受け取り、buildメソッドの中でattachで添付する、という処理になっています。

では、これを使用する処理を記入します。

use App\Mail\SendQRcodeMail;
use Mail;

    public function add($param, $room, $user)
    {
        Log::debug(print_r($param ,true));
        $model = new ReserveManagement;
        foreach($this->paramNames as $name)
        {
            $model->$name = $param[$name];
        }
        $model->lock_number = $this->generateLockNumber();
        QrCode::format('png')->size(100)->generate($model->lock_number, public_path('/img/qr.png'));
        $model->save();
        $this->attachToRoom($model, $room);
        $this->attachToSchedule($model);
        $this->attachToUser($model, $user);

        $to = [
            [
                'email' => $user->email, 
                'name' => $user->fullname,
            ]
        ];
        Mail::to($to)->send(new SendQRcodeMail('/var/www/html/hotel-mng/public/img/qr.png'));

    }

base64を添付しようと思いましたが、うまく行かなかったので、generateで画像をファイルに出力します。

Mailクラスを使用して、$toに宛先(ユーザー登録時に入力したものを使用します。)を設定し、sendで先程のクラスを使用します。

コンストラクタの引数には、画像のフルパスを設定します。

最後に、メールの本文。

[添付ファイル]

画像を表示できればいいので、シンプルなテンプレートにしました。

これをresources/views/emails/mail.blade.phpとして、保存します。

これで、正しく動いていれば、予約登録時にメールでQRコードが届きます。

【Laravel】【ホテル予約管理】解錠ナンバーをQRコードに変換する

前回までの状況はこちら。

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/hotel-mng

さて、今回は、前回作成したコードで生成した解錠コードをQRコードに変換します。

使用するのは、simplesoftwareio/simple-qrcodeというライブラリ。

Laravelのプロジェクトディレクトリにて、以下のコマンドを実行。

$ composer require simplesoftwareio/simple-qrcode

いま使用しているLaravelのバージョンは5.5.*なのでこれだけでライブラリが使用可能になります。

では、解錠コード生成処理を修正します。

use QrCode;

    public function add($param, $room, $user)
    {
        Log::debug(print_r($param ,true));
        $model = new ReserveManagement;
        foreach($this->paramNames as $name)
        {
            $model->$name = $param[$name];
        }
        $model->lock_number = $this->generateLockNumber();

        $src = base64_encode(QrCode::format('png')->size(100)->generate($model->lock_number));
        Log::debug(print_r($src ,true));
        $model->save();
        $this->attachToRoom($model, $room);
        $this->attachToSchedule($model);
        $this->attachToUser($model, $user);
    }

QrCodeクラスを使って解錠コードをpngファイルで生成、それをbase64に変換しています。

これは、とりあえずログに出力させています。

[2020-02-07 10:06:26] local.DEBUG: iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB3UlEQVR4nO2b24rEIBAFd8L+/yeHfRMWQbrsNjozVa+Ty1CcQ4zG133fPxLj2v0H3gllAZQFUBZAWQBlAZQFUBZAWQBlAZQFUBZAWQBlAZQFUBZAWYDfudOuK2W5Tc/21+lnbtsxg7Pm7k4xWQBlASZr2ECRHpRurlnJu1NMFkBZgGwNG4OQD8oyeNJVVaxwYdRkAZQFKKthFck+LsVkAZQFOKWGkZfE7ZgsgLIAZTWca01V157prMkCKAuQrWFy3iPybjgYneZnXRAmC6AswOuQsZ+D0k9DWYCF64ZoBTCycjH3WCyc8zFZAGUBsk/D8vX3yOlVV6aYLICyAPU17El2BL0ALh3KmiyAsgD1M6XJkSe6V4TC9UeTBVAWYPJpGMl2VfvQlOnS0anJAigLMPk0jCS5Pwa990V4+EsbkwVQFmDzDou5acy5L1EdlD6KsgCbd1hUHbzuPfTfX6q60DegLMDmHRYRkh/PuG64B2UBNn/aPahGcgEx8hPFZAGUBTh3h0WjamdxHpMFUBZg8w6LwemtUMkJmcJimiyAsgCbd1j010FLIX7Mdi7KApyyw+ItMFkAZQGUBVAWQFkAZQGUBVAWQFkAZQGUBVAWQFkAZQGUBVAWQFkAZQH+AEkJCJS/ZGd/AAAAAElFTkSuQmCC  

これは後ほど、HTMLメールのimgタグのsrc要素に書き込んで使用することを考えています。

これをブラウザで表示させてみましょう。

以下をアドレスバーに入力するとQRコードが表示されます。

data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB3UlEQVR4nO2b24rEIBAFd8L+/yeHfRMWQbrsNjozVa+Ty1CcQ4zG133fPxLj2v0H3gllAZQFUBZAWQBlAZQFUBZAWQBlAZQFUBZAWQBlAZQFUBZAWYDfudOuK2W5Tc/21+lnbtsxg7Pm7k4xWQBlASZr2ECRHpRurlnJu1NMFkBZgGwNG4OQD8oyeNJVVaxwYdRkAZQFKKthFck+LsVkAZQFOKWGkZfE7ZgsgLIAZTWca01V157prMkCKAuQrWFy3iPybjgYneZnXRAmC6AswOuQsZ+D0k9DWYCF64ZoBTCycjH3WCyc8zFZAGUBsk/D8vX3yOlVV6aYLICyAPU17El2BL0ALh3KmiyAsgD1M6XJkSe6V4TC9UeTBVAWYPJpGMl2VfvQlOnS0anJAigLMPk0jCS5Pwa990V4+EsbkwVQFmDzDou5acy5L1EdlD6KsgCbd1hUHbzuPfTfX6q60DegLMDmHRYRkh/PuG64B2UBNn/aPahGcgEx8hPFZAGUBTh3h0WjamdxHpMFUBZg8w6LwemtUMkJmcJimiyAsgCbd1j010FLIX7Mdi7KApyyw+ItMFkAZQGUBVAWQFkAZQGUBVAWQFkAZQGUBVAWQFkAZQGUBVAWQFkAZQH+AEkJCJS/ZGd/AAAAAElFTkSuQmCC

スマホで読み取ると、

データベース上では、

うまくできているようです!

【Laravel】【ホテル予約管理】クレジット情報を登録する(訂正)

前回までの状況はこちら

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/hotel-mng

さて、まずはユーザー登録時にクレジット情報を登録するようにしましょうか。

本来ならば、SSL通信によって、暗号化した状態でデータを送信しなくちゃいけないのですが、今はローカル環境でテスト用に動かしているので、そこら辺は考慮しないことにします。

考慮する場合は、NginxなどのWebサーバの設定が必要になります。

こちら、自前のクラウドサーバをHTTPSに対応した記事が参考になると思います。

それではコーディングしていきます。

やっていることは、以前にも書いた、usersテーブルにカラムを追加していく、ということです。

まずはマイグレーションの記述。

class AddCreditInfoUsers extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('credit_number')->after('phone');
            $table->string('mm')->after('credit_number');
            $table->string('yy')->after('mm');
            $table->string('credit_name')->after('yy');
            $table->string('code')->after('credit_name');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('credit_number');
            $table->dropColumn('mm');
            $table->dropColumn('yy');
            $table->dropColumn('credit_name');
            $table->dropColumn('code');
        });
    }
}

ビューの記述。

                        <div class="form-group{{ $errors->has('credit') ? ' has-error' : '' }}">
                            <label for="credit" class="col-md-4 control-label">credit number</label>

                            <div class="col-md-6">
                                <input id="credit" type="credit" class="form-control" name="credit" required>

                                @if ($errors->has('credit'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('credit') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group">
                            <label for="mmyy" class="col-md-4 control-label">MM / YY</label>

                            <div class="col-md-6">
                            <select name="mm">
                                <option value="1">01</option>
                                <option value="2">02</option>
                                <option value="3">03</option>
                                <option value="4">04</option>
                                <option value="5">05</option>
                                <option value="6">06</option>
                                <option value="7">07</option>
                                <option value="8">08</option>
                                <option value="9">09</option>
                                <option value="10">10</option>
                                <option value="11">11</option>
                                <option value="12">12</option>
                            </select>
                            /
                            {{Form::selectYear('yy', 2020, 2030)}}
                            </div>
                        </div>

                        <div class="form-group{{ $errors->has('credit_name') ? ' has-error' : '' }}">
                            <label for="credit_name" class="col-md-4 control-label">credit name</label>

                            <div class="col-md-6">
                                <input id="credit_name" type="credit_name" class="form-control" name="credit_name" required>

                                @if ($errors->has('credit_name'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('credit_name') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group{{ $errors->has('code') ? ' has-error' : '' }}">
                            <label for="code" class="col-md-4 control-label">security code</label>

                            <div class="col-md-6">
                                <input id="code" type="code" class="form-control" name="code" required>

                                @if ($errors->has('code'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('code') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

入力項目に、クレジット番号、有効期限(MM/YY)、名義、セキュリティコードを追加しました。

バリデーション処理です。

    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|string|max:255',
            'fullname' => 'required|string|max:255',
            'address' => 'required|string|max:255',
            'phone' => 'required|digits:11',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6|confirmed',
            'credit' => 'required|digits:16',
            'mm' => 'required|numeric',
            'yy' => 'required|numeric',
            'credit_name' => 'required|string|max:255',
            'code' => 'required|digits:3',
        ]);
    }

登録処理です。

    protected $fillable = [
        'name', 'fullname', 'address', 'phone', 'email', 'password', 'credit_number', 'mm', 'yy', 'credit_name', 'code'
    ];
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'fullname' => $data['fullname'],
            'address' => $data['address'],
            'phone' => $data['phone'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
            'credit_number' => encrypt($data['credit']),
            'mm' => $data['mm'],
            'yy' => $data['yy'],
            'credit_name' => encrypt($data['credit_name']),
            'code' => encrypt($data['code'])
        ]);
    }

クレジット番号、名義、セキュリティコードは機密性が高いため、encrypt()(※2020/2/5訂正)で暗号化してわからない形でデータベースに保存します。

※bcrypt()では、復号化できないようです。後ほどこの情報を使って自動決済する必要がありますので、復号化できないと困ります。

うまくできました。

【Laravel】【ホテル予約管理】次の課題は、自動決済を可能にする。

さて、お次の課題は、自動決済できるようにする、というものです。

具体的には、

  • クレジット情報を登録する
  • QRコードまたはナンバーを発行し、メールで送付する
  • QRコードもしくはナンバーで部屋のロックを解除し、このときに決済を行う。
  • ナンバーを3回間違えたら、ナンバーを再発行する。
  • 時間がすぎると、キャンセル料8割を決済する。

これらから、どのような実装を行うかを検討します。

クレジット情報は、ユーザーテーブルにカラムを追加するだけでいいのですが、通信はすべて暗号化する必要がありますね。

SSLが有効になるように、証明書の発行と、Webサーバの設定を変えなければいけません。

QRコードはナンバーをそのままQRコードに変換するだけで大丈夫でしょう。

幸い、LaravelにはQRコードを生成するライブラリもあるようです。

それをメールで送付しなければならないので、SMTPを使用しなければなりませんね。

QRコードの読み取りは外部のデバイスを使用する、と仮定して、実装はそのデバイスからナンバーを受け取るためのAPIを用意する必要があると思います。

あとは、コンソールコマンドを用意し、cronで定期実行できるようにすれば問題ないでしょう。

それでは次回から早速実装に入ろうと思います。

【Laravel】【ホテル予約管理】レスポンシブ対応を行う

前回までの状況はこちら

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/hotel-mng

さて、残る作業はレスポンシブ対応ですが、

そもそも、レスポンシブ対応とは何かというと、

PCだけでなく、スマホやタブレットでも、そのデバイスに対応した表示に切り替えることができる対応、ということです。

PCでは正しく表示できても、スマホだと表示が崩れて使用できない、というのはレスポンシブ対応とは言えません。

その確認のために重宝するのが、Google Chromeのレスポンシブモード。

擬似的にスマホやタブレットでの表示に切り替えてくれる機能です。

使い方は、Google Chromeの画面でF12を押し、

このボタンをクリックして青色(絵の状態)にします。

表示エリア上部にこんな感じのバーが表示されますので、ここをクリックすることで、画面の表示解像度を切り替えることができます。

こうやってレスポンシブ対応できているかを確認するのですね。

実機を使うと、それだけ実物のデバイスを用意しなければなりませんので、これはかなり有効な確認方法です。

ためしに、作成中のいろんなページを表示させてみましょう。

こんな感じで文字が2行になるのはまだ読めるのでセーフです。

こんな感じで表示が枠からはみ出てしまったらアウトですね。

修正する必要があります。

こういうのは、たいていテンプレートやスタイルの問題です。

こんな感じでどうでしょうか。

いい感じに仕上がったと思います。

実は、Laravelには、標準でBootstrapというJSライブラリが組み込まれていて、このライブラリでブラウザ差分を吸収したり、基本的なレスポンシブ対応を行えるようにしてくれています。

今作成している機能はBootstrapをバリバリ活用した作りになっていません。そもそもそんな表示をするところが無いためです。

もしかしたら、画像のサムネイルなどをたくさん表示させたり、表を二列に表示させる場合などに活用することになるかもしれませんね。

まぁ、これで最終チェックして提出しようと思います。