個人利用のアプリであるならば、データの保管場所はsqliteなどで良いかと思います。しかし、複数名共有して使うデータベースの場合には、やはりMySQLなどのDBサーバが必要になります。中央集権なこのDBがあることで、様々な社内ツールをElectronで実現する事ができます。

相手のサーバにWeb Serverがなくとも、Electron側がそれを担うので、非常に単純な構成でちょっとしたウェブアプリケーション的なクラサバ構築できるのは魅力的です。VBA卒業して挑戦するならElectronは良い選択肢だと思います。ウェブアプリと同じUI表現が利用できますし。

今回は、MySQL接続に加えて、DB接続パスワードなどを安全に格納できるように、keytarモジュールを利用してOS標準のキーストアを使ったセキュリティ対策も実施しておこうと思います。

図:今回作るアプリはこんな感じです

今回使用するモジュール他

Node.jsモジュール

  • keytarモジュール – OS標準のパスワード管理システムを利用して、安全にパスワード等のやり取りをする
  • electron-store – 各種設定情報を格納する為のモジュール
  • Promise-MySQLモジュール – Promiseが使えるMySQLへアクセスする為のモジュール
  • jQueryモジュール – ElectronでjQueryを使えるようにする為のモジュール
  • node-gyp – keytarモジュールをリビルドする為に使用する

HTML側で利用するライブラリ

使用するDBファイルとプロジェクトファイル

また、今回のサンプルアプリで用いたファイル類は以下からダウンロードできます。今回はローカル仮想環境内のMySQLを利用しましたが、Google Cloud SQLへ接続して利用も可能です。Cloud SQLの場合一旦、Cloud Storageにバケットを作成して、SQLファイルを配置しなければ、インポートが出来ないので注意(GB/月あたりの価格: $0.023)。なぜか1回目はインポート失敗するけれど、2回目は成功します。

図:Cloud StorageのバケットからSQLファイルインポート中

図:Cloud SQLにインポートしてみた

事前準備

モジュールを追加する

まずは、プロジェクトファイル作成、package.json作成、index.htmlおよびindex.jsを作成して置きます。ターミナルを起動して以下のコマンドで今回利用する予定のモジュールを入れておきましょう。

promise、promise-mysql、keytarの3つを追加しました。

keytarについて

keytarの概要と使い方

現代のOSには、OS標準のパスワード管理の為のセキュリティシステムが用意されています。macOSで言えば「keychain」、Linuxならば「libsecret」や「Gnome Keyring」、Windowsですと「Credential Vault(資格情報マネージャ)」がそれになります、keytarはElectronでセンシティブな情報を扱う場合に、ローカルストレージではなく、こうした管理システムへの情報の登録と呼び出しをサポートする為のモジュールです。

接続パスワードの保管や、OAuth2認証で使うClient_IDとSecretの格納、Access_TokenやRefresh_Tokenの格納などを安全に格納する事が可能で、この手のアプリケーションを作る上では必須とも言える機能ですね。

メソッドもえらくシンプルで

が基本。他に削除、検索のコマンドがあります。但しパスワードの取得だけは少しだけコードが異なります。非同期処理なので、注意してください。

もしくはIPC通信を利用して

といった取り出し方をすると良いでしょう。

Linuxに於けるKeytarの問題点

まだ、Linux上でElectronのビルドを試してはいないのですが、少なくともNode.jsアプリとして、Ubuntu 18.04上でkeytarモジュールを動かす為には、以下のライブラリをインストールしておかなければなりません。

macOSに於けるkeytarの問題点

また、環境によってはそのままだと動かないことがあるので、keytarモジュールをリビルドする必要があります。表示されるエラーは以下のような感じ。electron v4.0.3, keytar 4.3.1でテストしています。

この問題は、素で入れたkeytarがelectronのバージョンに合っていない為に起こる問題で以下のような形でリビルドをすると、osxであれば無事にキーチェーンに資格情報が格納されます。

  1. electronのバージョンを、electron -vで調べておく
  2. 使用している環境が32bit環境下?64bit環境かしらべておく。通常今どきは64bitだと思うので、x64だと思います。
  3. ターミナルを立ち上げて、プロジェクトフォルダの中のnode-modulesフォルダ、さらに其の中のkeytarフォルダに入ります。
  4. 以下のコマンドを実行する(electronのバージョンは4.0.2で、アーキテクチャはx64で指定しています。)

electronで実際にリファレンスに従い、setPasswordをしてみたところキーチェーンに無事に資格情報が格納されました。getPasswordで取り出す事が可能です。

図:keytarをリビルド中

図:zasekiというサービス名にアカウント名とパスワードが格納される

Windowsでkeytarを使う場合

macOSの場合は前項のようにkeytarモジュールをリビルドするだけで利用する事ができました。しかし、問題はWindows。そもそもリビルドするにもビルド環境が必要だったり、リビルドしても動かずに停止してしまったりと、問題解決が非常に大変でした。すごく良いモジュールなのに、この問題が残念ですね。。。ネイティブモジュール特有の問題です。

結果的には、electronはv3.0.0keytarは4.2.1を利用し、electron-rebuildを使ってのリビルドで使えるようにはなりました。以下に使えるようにする為の手順を残しておきます。参考になったのは、こちらのサイトと、issue議論のサイトissue議論サイト2バージョン指定でインストールする方法サイト、node-gypインストール方法サイトでした。

  1. electronは一旦、npm uninstall -g electronでアンインストールする
  2. keytarも一旦、プロジェクトフォルダに入ってから、npm uninstall keytarでアンインストールする(Windowsでは、keytar v4.3.0は問題有り
  3. npm install -g electron@3.0.0でバージョン指定でインストール
  4. keytarをインストールする前に、管理者権限のPowerShellにてnpm install –global windows-build-toolsを実行して、インストールする(結構時間が掛かる)
  5. Visual C++ Buildtoolsをインストールする(Visual Studio 2017相当)
  6. npm config set msvs_version 2017コマンドを実行
  7. エクスプローラの検索にて、python.exeがある場所を探しだし、npm config set python python.exeのフルパス コマンドを実行する
  8. npm install -g node-gypで、node-gypをインストール
  9. npm install -g electron-rebuildで、electron-rebuildをインストール
  10. プロジェクトフォルダに入り、npm install keytar@4.2.1を実行して、ネイティブコンパイルインストールする
  11. keytarディレクトリに入り、node-gyp configureを実行する
  12. electron-rebuild -w keytarでリビルドをする
  13. これで、Windowsでもkeytarが使えるようになり、アプリが落ちることがなくなりました。
  14. 試しにkeytarを使ってパスワードをセットしてみて、該当の設定がWindows資格情報に入っていれば成功!!

図:Windowsの資格情報マネージャに登録できた

※keytar4.3.0やelectron4.0.3でもelectron-rebuildをすれば使えるようになるかもしれませんが、試していません。とりあえず、動く環境にたどりつけたので良かったです。

※keytarを使ったアプリをmacOSとWindowsの両方でリリースする場合は、それぞれに環境を作ってビルドするほうが良いと思います。

社内で使うと接続が途切れる場合

小さな企業内の場合、せいぜいルーターが1個ある程度の環境なので、普通にサーバを立てれば普通に運用が可能だと思いますが、ある程度の組織の場合、途中に存在するルータ、スイッチその他いろいろなものが入っています。ネットワークの設計上早めにTCPのコネクションを解除するような設定が入っていると、立てたMySQL Serverに他の方が接続できなくなったり、しにくくなったりすることがあります。

そこで、OS側に定期的に生存している旨のパケットを送る設定がありますが、これを少し変更しておくと接続を維持できるようになります。もちろん、OS自体が勝手にスリープしてしまったり、サスペンドしないように設定しておくようにしましょう。今回はUbuntu Linuxをベースに記述します。これらの作業はターミナルとテキストエディタで行います。

logind.confでサスペンドをオフ

geditが起動したら、以下の項目を新規に追記

追記したら再起動すればサスペンドしないようになります。

InterfacesにDNS設定

geditが起動したら以下の項目を新規に追記。すでにあるdns-nameserversはコメントアウト。DNSは社内にあるならそれを指定。

追記したら再起動するだけです。

nsswitch.confでhostsの設定

geditが起動したら以下の項目に編集しなおす。

追記したら再起動するだけです。

resolv.conf関係にDNSを設定

geditが起動したら、以下の項目を追記する

さらに以下のコマンドで編集を行う

起動したら既存のdns-nameserversをコメントアウトして、以下の項目を追記

追記したら再起動するだけです。

sysctl.confでkeepaliveの設定

最後に一番重要な設定。キープアライブの設定とIPv6の設定を行います。UbuntuはIPv6の設定をオフにしておきます。

geditが起動したら以下の項目を追記する

追記したら以下のコマンドで設定を反映する。sysctl -a | grep keepalive_timeは保存した設定を確認するコマンドです。

これで即時にキープアライブとIPv6の設定が反映されます。

MySQL側の準備

MySQL Serverのインストール

今回、仮想環境上のLinuxやmacOS上に、MySQL Serverをインストールして開発を行っております。今回はmacOSで以下の手順でインストールしております。以前、XAMPPを使っていた関係で、以前のバージョンのMySQLが残っていたため、トラブルになりつつも、無事にサーバを用意する事ができました。

  1. macOSでのMySQLはHome Brewを使ってのインストールが一般的ですね。まずはターミナルを起動します。
  2. brew updateでアップデートをまずは実行
  3. 続けて、brew install mysqlで本体をインストールします。
  4. 完了したら、mysql_secure_installationコマンドでセットアップを開始するのですが、今回エラーが出ました。「Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’」
  5. sudo touch /tmp/mysql.sockでソケットファイルをつくってあげる
  6. また、作成したソケットの権限を、sudo chown ユーザ名 /tmp/mysql.sockで変更
  7. mysql_secure_installationコマンドで、セットアップ開始。rootのパスワード設定他、基本的にはYで回答しておけばオッケー。
  8. ERROR! The server quit without updating PID fileというエラーが出る場合には、以下のコマンドを実行して、mysqlを一度再インストールすると良いです。

brew uninstall mysqlでアンインストール可能です。macOSでHomeBrewで入るバージョンは、5.7ではなく8.0系なので注意が必要です。

テーブルの定義

今回は以前、Google Apps Scriptにて作成した座席表アプリを、MySQL + Electronで移植をしてみようと思います。MySQL側では、スプレッドシートで構成したシートレイアウトを元に、予めテーブルを作っておきましょう。自分が用意した構成は以下の通りです。

  1. MySQLに新たなschemeとして「zaseki」を作成
  2. zasekiスキーマに対して、「ユーザ表」と「シートデータ」のテーブルを準備
  3. MySQLにログインするユーザアカウントの作成とパスワードの設定を済ませておく
  4. ユーザ表はuseridとして定義、シートデータはseatとして定義しました。
  5. 2つのテーブルには3.で作ったアカウントでのアクセス権限は付与しておきましょう。

図:新しいテーブルスキーマを作成

図:ユーザ表のテーブル定義

図:シート表のテーブル定義

MySQLのTimeoutを設定等

MySQLを動かすUbuntu上のサーバでいろいろとKeepaliveの設定を行っていても、MySQLにはMySQLのコネクションの処理に関する設定があります。こちらも設定しておくべきでしょう。

skip-resolve-name

my.cnfもしくはmysqld.cnfに対して、skip-resolve-nameの設定を追記します。これは固定IPで運用する場合に設定するもので、DNSでの逆引きを利用しない設定です。少しだけ早くなると同時に余計なDNS参照をする事がなくなります。単純にskip-resolve-nameを追記するだけでオッケーです。

ただしこの設定を追加すると、localhostでの接続ができなくなります。127.0.0.1での接続は可能ですが、MySQL側で例えば、root@localhostでアカウントを作っている場合、接続出来ません。root@127.0.0.1で作成している場合には接続が可能です。

wait_timeout

MySQLに接続する時に用いられる、接続してからの維持時間がコレです。デフォルトでは8時間(28800秒)で設定されています。正直これは長すぎるので、短く設定してみたいと思います。

これを300秒とする場合には、my.cnfもしくはmysqld.cnfに対して、wait_timeout=300を追記するだけです。

ソースコード

ログイン画面の作成(setting.html)

  • メイン画面の子Windowとして呼び出されます。macOSの場合は上からにゅっと出てくるようになるのですが、Windowsの場合はモーダルダイアログとして表示されます。
  • サーバアドレス、使用DB名、ユーザID、パスワードを入力し、メインプロセスのkeytarにて情報をOS管理の資格情報システムに登録します。また、起動時には資格情報システムから呼び出してロードします。
  • 今回の画面のCSSはこちらのサイトのスタイルシートを利用しています。

メイン画面の作成(index.html)

  • メインの座席表を表示するレンダラープロセスのコードです。
  • 座席確保やリリース時に入力するダイアログでの値はLocalstorageに保存されます。画面表示する際には呼び出されます
  • 座席表の基本データはzaseki.htmlに記述してあり、起動時にidがzasekinの場所にロードされます。ロードが完了してから、shapeshifterの適用を行い、またMySQLから現在のシート確保状況データをconnectSQLでロードさせています。
  • 座席のレイアウトや確保対象外とするパネルはzaseki.html側を編集します。nothingが1のものは、確保対象外のパネル(非表示になります)を意味します。cabinetはそもそも椅子ではない場所を意味します。data-ss-colspanは横長のパネルで役員席などに適用する為のものです(横に2パネル消費します)
  • testsync関数では、メインプロセスからの通信を受け取るipcrenderer.onを待機させる為の関数です。各関数はメインプロセスからプッシュされると自動的に応答して処理がなされます。
  • メインプロセス側にデータを送る役目をしてるのが、ipcRenderer.sendでメインプロセス側で受け取ると処理が開始されます。

Node.js側コード(index.js)

  • keytar自体はモジュールが動いてくれる状況であれば、OSに合わせてOSに装備されてる資格情報管理システムへパスワードを記録してくれます。
  • 今回、やや冗長なコードの書き方になっています。
  • 接続設定用のsetting.htmlおよびメイン表示用のindex.htmlの2つによるマルチウィンドウ仕様ですが、setting.html側には初期ではhideになるようにし、またindex.htmlの子ウィンドウになるようbrowserwindowにオプション設定を追加してあります。
  • ipcMain.onにてレンダラープロセス側からの通信を待機させてあります。
  • promise-mysqlを利用しているので、同期処理でMySQLへの接続を実現しています。
  • MySQL接続とクエリの処理では、通常のSQL文を使った処理と、where条件に?を使うプレースホルダーを使ったケースの2つで記述をしています。プレースホルダーを使ったほうが引数を渡しやすいですね。SQLインジェクション対策も考えたら、プレースホルダと変数のチェック機能をつけるべきでしょう。
  • keepsheetとrelsheetの2つでは確保状況の確認と実際に値をセットするSQLの2回を連続実行しています。promiseで順番に実行させられるので、コールバック地獄にならず綺麗に書けます。
  • relsheetsqlにてケースによって確保した座席がリリースされない場合があるので修正しました。

指定時刻に座席を強制リリース

今回のアプリケーションは、誰かが座席を確保したまま、リリースを忘れると永遠に席を確保された状態になってしまいます。それでは翌日皆が困ることになりますね。そこで、この座席についてkoteiflgが0の値(つまりフリーアドレス席全部)について、すべての座席データを強制リリースさせる為の仕組みが必要になります。

この作業もNode.jsでやらせる事が可能です。MySQL Serverの入ってるマシンにNode.jsをインストールして、座席を強制リリースするSQL文を書いたアプリをサービスとして常駐させて置きます。今回は、Ubuntu Linux 18.04をベースにお話を進めます。

事前準備

electronでもNode.jsを利用しましたが、今回のアプリでもNode.jsを利用します。今回追加で利用するモジュールは以下の通り。クライアントアプリも作れ、サーバサイドも同じ言語で作れるNode.jsの美味しいポイントですね。

これらをnpm installでプロジェクトフォルダ内で実行しインストールしておきましょう。foreverはプログラム中で使うのではなく、外部で利用するものなので、-gオプションでグローバルインストールしましょう。

ソースコード

  • keytarモジュールは今回とくにリビルドせずとも動作しました。electron上で動かす時とは違って素直でした。
  • zaseki_clearというサービス名を自分は使って、資格情報にパスワードを登録しています。初回だけこのパスワード格納の作業が必要です。次回以降はkeytarモジュールが自動で取得してくれます。
  • 座席のクリアは、UPDATE文の発行で処理します。koteiflgが0のレコードを対象にします。
  • node-cronにてCronのように指定時刻に特定のコマンドを実行します。このアプリは一度起動するとシャットダウンされるまで自動で、常駐し続けて動作します。

図:パスワードと鍵にて登録内容を確認できる

デーモン化

今回のこのアプリですが、このままでは、起動する為には手動でコマンド入力が必要です。そこで、このアプリをOS起動時に自動的に起動し常駐させておく仕組みも用意します。foreverモジュールを利用すると、作成したJSファイルをデーモン化して管理することが出来るようになります。複数のNode.jsアプリケーションを起動し常駐させることができ、またスクリプトが落ちた時に自動的に再起動もしてくれます。Node.jsを扱うなら是非入れておきたモジュールの一つです。デーモン化自体は以下のコマンドで可能です

この時、オプションなどを指定することが出来ます。ファイルの指定は拡張子まで含めて行わなければなりません。プロセスは、forever listコマンドで一覧が出てきます。通常はここから、systemdなどに登録してこのスクリプトを自動起動したいところなのですが、今回のスクリプトは、keytarを使ってパスワードを取得する必要があるため、最初の1回はforeverでstartする際にsudoでのログインパスワードを求める画面が出るので、systemdでの自動化が出来ません。

図:デーモン起動時にパス取得の問い合わせが出る

図:forever listで現在のデーモンを確認

そこで利用するのが、gnome-session-properties。これは、Ubuntuが起動後に自動的に起動するアプリケーションを登録しておく為のもので、systemdのような仕組みというより、Windowsのスタートアップフォルダみたいなものです。ここで以下のように登録しておきます。

  1. 右下の●の粒をクリックする
  2. アプリケーション一覧が出てくるので、検索画面でsessionと入れる。「自動起動するアプリケーションの設定」が出てくるので、クリックする
  3. 追加ボタンをクリック
  4. 適当な名前、実行するコマンドの入力(今回はシェルスクリプトを作ってそれを登録してあります)します。
  5. 保存を押して完了

これで、毎回ログイン後に、forever start index.jsの入ったシェルスクリプトが起動し、パスワード入力を求めてきます。パスワードを入れて実行すると、keytarがlibsecretからパスワードを拾ってきてforeverにてindex.jsのアプリが常駐化します。

図:アプリやシェルスクリプトをX起動後に実行します。

きちんとパーミッションやこのシェルスクリプトの内容が正しければ、再起動した直後にforever listコマンドを叩くと、Node.jsアプリのプロセスが出てくるはずです。また、Node.jsのアプリがこけて自動再起動が掛かると、PIDが変わります。

  • どうも、昔はNode.jsは起動しっぱなしだとメモリリークでどんどんメモリを食いつぶすとか、それは直ったといった話があったようです。node –expose_gc setseat.jsといったようなオプションを付けて起動すると良いとかなんとか。
  • 今回のスクリプトは1回手動でパスワード登録が必要になっているので、node index.jsにて起動し、パスワードが無事に登録出来ているかを確認しましょう。次回以降はパスワードを自動でロードされると思います。

実行ファイルと結果

実行ファイルのダウンロード

今回のファイルをそれぞれの環境でelectron-packagerでビルドしてみました。無事にローカルのMySQLおよびGoogle Cloud SQL、Cloud SQL Proxy経由で接続が成功しました。

図:Windowsでも無事に動きました。

こんな感じに動きます

関連リンク

共有してみる: