- 投稿日:2020-06-30T23:22:27+09:00
React.jsをDockerで動かした時のメモ
環境
$ cat /etc/linuxmint/info RELEASE=19.3 CODENAME=tricia EDITION="Cinnamon" DESCRIPTION="Linux Mint 19.3 Tricia" DESKTOP=Gnome TOOLKIT=GTK NEW_FEATURES_URL=https://www.linuxmint.com/rel_tricia_cinnamon_whatsnew.php RELEASE_NOTES_URL=https://www.linuxmint.com/rel_tricia_cinnamon.php USER_GUIDE_URL=https://www.linuxmint.com/documentation.php GRUB_TITLE=Linux Mint 19.3 CinnamonDockerのインストール
$ apt update $ apt install docker-ce docker-ce-cli docker-composenode.jsとcreate-react-appのインストール
$ apt install nodejs $ npm install create-react-app yarnサンプルReact.jsアプリを作る
$ create-react-app docker-test $ cd docker-testとりあえず普通に起動してみる。
yarn startブラウザで http://localhost:3000 にアクセスしReact.jsが動作していることを確認。
確認できたらCtrl-Cで落とす。Dockerfileとdocker-compose.ymlを作成
作成したReactアプリのルートディレクトリに以下のような内容で
Dockerfile
とdocker-compose.yml
を作る。DockerfileFROM node:14.4.0-buster-slim WORKDIR /usr/src/app RUN npm install --save prop-types RUN npm install -g create-react-appdocker-composeversion: '3' services: node: build: context: . tty: true environment: - NODE_ENV=production volumes: - ./:/usr/src/app command: sh -c "yarn start" ports: - "3000:3000"上記のような内容だと、作成したReactアプリのルートディレクトリがDocker環境内では
/usr/src/app
にマウントされるため、 WORKDIRを/usr/src/app
に指定してあれば、実行するスクリプトはsh -c "yarn start"
だけとなるため、これをcommand
で指定している。docker-hub nodeを見に行くと、
alpine
buster
stretch
などの名前がバージョンについています。なんのことかと思ったらLinuxディストリビューションですね。
slim
とついているものは、Debianディストリビューションでエクストラパッケージが含まれないものです。いずれにしても今回はDockerでnodeが動けばいいのでnode:14.4.0-buster-slim
を選択しました。
ちなみにDebianの現在のバージョンは buster(10.0) であり、それ以前は以下のようになります。Debian 10 (buster) — 現在の安定版リリース Debian 9 (stretch) — 過去の安定版リリース Debian 8 (jessie) — 過去の安定版リリース Debian 7 (wheezy) — 過去の安定版リリースalpine linuxはパワーユーザー向けの軽量Linuxのようです。軽量だけにイメージが小さく抑えられるかもしれませんが、私はDebianやUbuntuしか使ったこと無いので
alpine
の使用はやめておきました。Dockerイメージをビルド
$ docker-compose buildDockerでReact.jsアプリを実行する
起動
$ docker-compose upブラウザで http://localhost:3000 にアクセスしReact.jsが動作していることを確認。
Ctrl-C
で停止。バックグラウンドで起動
$ docker-compose up -d停止
$ docker-compose downイメージ情報を確認
$ docker images | grep dockertest dockertest_node latest daf22d9465c7 26 minutes ago 194MBおまけ
すべてのDockerイメージを停止
$ docker stop $(docker ps -q)
- 投稿日:2020-06-30T23:08:09+09:00
Node.jsのExpressでbody-parserを使いたくないけど特定のハンドラーでだけボディのパースをしたい
特定のURL以下だけパースしたいが、その他の広いパスではパースしたくないみたいなことが5年に1度ぐらい発生するかもしれないので、そのやり方のメモ。例えば99%のリクエストはプロキシーとしてそのまま他のプロセスに流す場合、パース作業はほぼ無駄になってしまうので必要なところでだけやりたいですよね?routerを作ってその下だけbody-parserを適用とかできるならそれでもいいかもしれませんが、微妙にそれもしにくい・・・みたいな場合に。
app.tsimport express, { Request, Response } from 'express'; // 本当はimportしなくてもいいはずだけどTypeScriptはimportしないとエラーになるっぽい import { URLSearchParams } from 'url'; app.post('/form/receive', (req: Request, res: Response) => { let body = ''; // streamのAPIを直接扱ってdata/endで情報を読み込む req.setEncoding('utf8'); req.on('data', (chunk) => { body += chunk; }); req.on('end', () => { // フォームのポストを受け取るのでURLSearchParams. // JSONならJSON.parse const params = new URLSearchParams(body); console.log(params); // パースできてる! res.end(); }); }); const port = process.env.NODE_PORT || 3000; console.log(`listening at ${port}`);` app.listen(port);
- 投稿日:2020-06-30T22:49:29+09:00
[Alexa]stylesを使って押したら色が変わるボタンを作成する
stylesを使うとうれしいこと
APLのドキュメント部では、layoutsやcommandsのように共通要素を外だしすることができます。
これと同様に、stylesを使用すると、コンポーンントの特定プロパティの値だけを取り出して定義・共通化することができます。
さらに、stylesでは「対象のコンポーネントが押下されたときだけ色を変える」といったことも可能です。
本記事では、APLでstylesを利用した例を挙げてみようと思います。APLドキュメントファイルの作成
以下は、stylesを利用したAPLドキュメントファイルの例です。
"styles"にてスタイル"pushButtonStyle"を定義し、Frame要素にて"pushButtonStyle"を適用しています。StyleTestTemplate.json{ "type": "APL", "version": "1.3", "settings": {}, "theme": "dark", "import": [], "resources": [], "styles": { "pushButtonStyle": { "value": [ { "backgroundColor": "#564387" }, { "when": "${state.pressed}", "backgroundColor": "#ac8fee" }, { "when": "${state.focused}", "borderColor": "#ffffff", "botderWidth": 10 } ] } }, "onMount": [], "graphics": {}, "commands": {}, "layouts": {}, "mainTemplate": { "parameters": [ "payload" ], "items": [ { "type": "Container", "width": "100vw", "height": "100vh", "alignItems": "center", "items": [ { "type": "TouchWrapper", "width": "50%", "height": "20%", "items": [ { "type": "Frame", "style": "pushButtonStyle", "inheritParentState": true, "width": "100%", "height": "100%", "items": [ { "type": "Text", "width": "100%", "height": "100%", "textAlign": "center", "text": "色が変わるよ!", "textAlignVertical": "center" } ], "borderWidth": "2" } ] } ], "justifyContent": "center" } ] } }こちらの例では、
・普段は背景色が#564387
・pressed時( = 押された時)は背景色が#ac8fee
・focused時( = フォーカスが合った時)は枠の色が#ffffff
というスタイルを定義し、Frameにセットしています。上記jsonではFrameの"inheritParentState"がtrueに設定されており、親コンポーネントであるTouchWrapperの状態を継承するため、押された状態やフォーカスがあった時には色が変化します。
他に、styles関連でのポイントとしては以下あたりが挙げられます。
・スタイルには、こちらの「スタイル設定可能なプロパティ」に該当するプロパティのみ設定できる。
・上記に該当するプロパティであっても、コンポーネントが持っていないプロパティは反映されない。
例:Containerに対して"backgroundColor"の値がセットされているスタイルを設定しても背景色は変わらない
・コンポーネントに直接値がセットされているプロパティは、スタイルに記載された設定よりも優先される。
(→上記の例だと、Frameに直接backgroundColorの値をセットしてしまうと、StyleのbackgroundColorは機能しなくなる)Node.js実装例
Node.jsに実装すると、以下のような感じになります。
上記のStyleTestTemplate.jsonがaplフォルダ内に配置されている前提となります。index.js// 対話モデルにてStyleTestIntentを作成しておく // StyleTestIntent発話時にこのハンドラに入る想定 const StyleTestIntentHandler = { canHandle(handlerInput) { return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest' && Alexa.getIntentName(handlerInput.requestEnvelope) === 'StyleTestIntent'; }, handle(handlerInput){ let st = 'スタイルのテストです。'; // APLドキュメントとデータソースを取得 const StyleTestTemplate = require('./apl/StyleTestTemplate.json'); // 画面作成 ※実際はAPL対応デバイスかどうかの確認が必要 handlerInput.responseBuilder.addDirective({ type: 'Alexa.Presentation.APL.RenderDocument', version: '1.0', document: StyleTestTemplate, datasources: {} // データソース部不要のため空のオブジェクトをセット }); return handlerInput.responseBuilder .speak(st) .reprompt(st) .getResponse(); } } // 以下略…この状態でスキルを実行すると、スタイル設定が反映されたボタンが画面に表示されると思います。
また、ボタンをタッチするとボタンの色が変わります。
補足
「focusedってどういう状態だよ、pressedは分かるけどフォーカスが合ってる状態ってなんだよ」と思う方もいるかもしれません。
FireTVからこのAPLで描写した画面を開いて見てみると、分かると思います。
FireTVをお持ちでない方向けに簡単に説明すると、
以下のような感じでリモコン操作のUIを構築することができます。
こういうこともできるって知ってた?#AlexaDevs #APL pic.twitter.com/XB8oPoRawi
— イガラシさん (@IgarashisanT) June 13, 2020参考URL
APL Style Definition and Evaluation | Alexa Skills Kit
APLスタイル設定可能プロパティ | Alexa Skills Kit
- 投稿日:2020-06-30T22:25:16+09:00
express+postgreSQL+Herokuでアプリ公開した手順
使用するパッケージがexpress,pg(とejs)のみで作成したWebアプリを、Herokuにて公開するまでをまとめました。git自体の詳細は省いたりしていますが、基本この通りやっていけば公開まではいけると思います。
全体的に情報が古めだったけど、良いサイトないかな。OS:Windows10
terminal:powershell
テキストエディタ:VScode1.準備
1-1.ローカル環境でexpress+postgreSQLのアプリ作成
この記事は主にローカル環境で出来上がっているけど公開の手順が分からない人向けなので、ここは省略します。
1-2.Herokuアカウント作成
1-3.HerokuのCLIを使えるようにする。
https://devcenter.heroku.com/articles/heroku-cli
でDLしたあとpathを通す。terminal> heroku -v heroku/7.42.1 win32-x64 node-v12.16.2などでバージョン確認ができればok。
2.Herokuアプリに接続する
ここから先はだいたいこのサイトを参考にしてます。追加情報が必要なときのみその時の参考サイトも貼りました。
https://tacamy.hatenablog.com/entry/2013/02/16/2351272-1.Herokuへログインする
terminal> heroku loginブラウザへ飛ばされたあと、そこに「成功」的な画面が出ていればok。
2-2.Herokuにアプリ作成
terminal> heroku createアプリ名にこだわるなら別の手順が必要ですが、僕はどうせheroku.appってurlに入るからどうでもいいかなと思って素のままいきました。
2-3.Procfile作成
app.js(自分のjsファイル名)が置かれている層にPricfile(拡張子なし)というページを作り、以下の内容のみ記述します。
Procfileweb: node app.jsWSGIという、Webアプリを動かす指示書的なものらしい。詳しくはhttps://creepfablic.site/2019/05/01/heroku-procfile/
2-4.gitignore作成
先程のProcfile,app.jsと同じ層に.gitignoreを作成して、以下の内容を記述。nodeのパッケージ等はHeroku側で用意されているので、gitでアップロードしないように制御する。
.gitignorenode_modules
現在はこのような位置関係になっていると思います。
-app.js
-package.json
-Procfile
-.gitignore
-node_module
-public
...etc2-5.package.jsonにengineの記述追加
特定のバージョンのみでしか動かないようにするために、package.jsonのdependenciesの後に以下の内容を記述。
package.json... "dependencies": { "ejs": "^3.1.3", "express": "^4.17.1", "pg": "^8.2.1", }, "engines": { "node": "^12.17.0", //自分のバージョン "npm": "^6.14.4" // 自分のバージョン } ...https://qiita.com/suin/items/994458418c737cc9c3e8
3.HerokuのDBへ接続する
3-1.Heroku上でDBを追加
Herokuのサイトに行き、アプリ名をクリック→Resources→Add-onsからHeroku Postgresを検索し、追加
3-2.各種データの確認
SettingsのReveal config varでURLを確認し、各種必要な値を得て、ローカル環境でのものと置き換える。
URLの構成:postgres:// ユーザー名:パスワード @ホスト名:ポート番号/データベース名app.jsconst pool = new Pool({ user: 'ユーザー名', host: 'ホスト名', database: 'データベース名', password: 'パスワード', port: 5432, //ポート番号 });https://qiita.com/shosho/items/5ebabf11efb1f3b604f7#url%E3%81%AE%E6%A7%8B%E6%88%90
3-3.portをHeroku用に変える
ローカル環境だとapp.listen(3000)みたいになってるところを以下の内容に変更。
app.jslet port = process.env.PORT || 3000; app.listen(port, () => { console.log("Listening on " + port); //ここは要らないけど一応 });適当な人のgithubからパクった。
https://github.com/FTraian/node-express-postgresql-heroku-tutorial/blob/master/web.js4.gitでファイルをアップロードする
4-1.git commitまで
terminal> git init > git add . > git commit -m "first commit"4-2.公開鍵を設定(一番面倒でした)
ここは僕自身理解度が低いですが、ひとまず僕が成功した手順を書いておきます。
まず、公開鍵が設定されていないことを確認します。terminal> heroku keys ! You have no SSH keys.確認できたら、ssh公開鍵を作成します。"xxx@gmail.com"の部分はherokuに登録したメールアドレスを入力します。
terminal> ssh-keygen -t rsa -C "xxx@gmail.com"すると以下のようにでてくるので、Enterキーを押したりpassphraseを設定したりします。
terminalGenerating public/private rsa key pair. Enter file in which to save the key (C:\Users\ユーザー名/.ssh/id_rsa): //そのままEnterキー Created directory 'C:\Users\ユーザー名/.ssh'. Enter passphrase (empty for no passphrase): //パスフレーズを入力(忘れないように)
その後、Herokuに登録して完了です。
terminal> heroku keys:add C:\Users\ユーザー名\.ssh\id_rsa.pub //id_rsa.pubがあるファイル名を指定 > heroku keys === xxx@gmail.com keys ...(公開鍵) xxx@gmail.comhttps://daipresents.com/2012/post-5010/
https://knowledge.sakura.ad.jp/3543/4-3.git push
初回のみ以下のコマンド。
terminal> git remote add heroku git@アプリ名.gitあとはいつもどおり。
terminal> git push origin masterここまでで、自分のアプリのURLをクリックしたら開けると思います。ありがとうございました。
おまけ
ローカル環境からDBをいじるには
terminal> heroku pg:psql -a アプリ名 アプリ名::DATABASE=>
- 投稿日:2020-06-30T22:25:16+09:00
express+postgreSQL+HerokuでWebアプリを公開した手順
使用するパッケージがexpress,pg(とejs)のみで作成したWebアプリを、Herokuにて公開するまでをまとめました。git自体の説明は省いたりしていますが、基本この通りやっていけば公開まではいけると思います。
全体的に情報が古めだったけど、良いサイトないかな。OS:Windows10
terminal:powershell
テキストエディタ:VScode1.準備
1-1.ローカル環境でexpress+postgreSQLのアプリ作成
この記事は主にローカル環境で出来上がっているけど公開の手順が分からない人向けなので、ここは省略します。
1-2.Herokuアカウント作成
1-3.HerokuのCLIを使えるようにする
https://devcenter.heroku.com/articles/heroku-cli
でDLしたあとpathを通す。terminal> heroku -v heroku/7.42.1 win32-x64 node-v12.16.2などでバージョン確認ができればok。
2.Herokuアプリに接続する
ここから先はだいたいこのサイトを参考にしてます。追加情報が必要なときのみその時の参考サイトも貼りました。
https://tacamy.hatenablog.com/entry/2013/02/16/2351272-1.Herokuへログインする
terminal> heroku loginブラウザへ飛ばされたあと、そこに「成功」的な画面が出ていればok。
2-2.Herokuにアプリ作成
terminal> heroku createアプリ名にこだわるなら別の手順が必要ですが、僕はどうせheroku.appってurlに入るからどうでもいいかなと思って素のままいきました。
2-3.Procfile作成
app.js(自分のjsファイル名)が置かれている層にPricfile(拡張子なし)というページを作り、以下の内容のみ記述します。
Procfileweb: node app.jsWSGIという、Webアプリを動かす指示書的なものらしい。詳しくはhttps://creepfablic.site/2019/05/01/heroku-procfile/
2-4.gitignore作成
先程のProcfile,app.jsと同じ層に.gitignoreを作成して、以下の内容を記述。nodeのパッケージ等はHeroku側で用意されているので、gitでアップロードしないように制御する。
.gitignorenode_modules
現在はこのような位置関係になっていると思います。
-app.js
-package.json
-Procfile
-.gitignore
-node_module
-public
...etc2-5.package.jsonにengineの記述追加
特定のバージョンのみでしか動かないようにするために、package.jsonのdependenciesの後に以下の内容を記述。
package.json... "dependencies": { "ejs": "^3.1.3", "express": "^4.17.1", "pg": "^8.2.1", }, "engines": { "node": "^12.17.0", //自分のバージョン "npm": "^6.14.4" // 自分のバージョン } ...https://qiita.com/suin/items/994458418c737cc9c3e8
3.HerokuのDBへ接続する
3-1.Heroku上でDBを追加
Herokuのサイトに行き、アプリ名をクリック→Resources→Add-onsからHeroku Postgresを検索し、追加
3-2.各種データの確認
SettingsのReveal config varでURLを確認し、各種必要な値を得て、ローカル環境でのものと置き換える。
URLの構成:postgres:// ユーザー名:パスワード @ホスト名:ポート番号/データベース名app.jsconst pool = new Pool({ user: 'ユーザー名', host: 'ホスト名', database: 'データベース名', password: 'パスワード', port: 5432, //ポート番号 });https://qiita.com/shosho/items/5ebabf11efb1f3b604f7#url%E3%81%AE%E6%A7%8B%E6%88%90
3-3.portをHeroku用に変える
ローカル環境だとapp.listen(3000)みたいになってるところを以下の内容に変更。
app.jslet port = process.env.PORT || 3000; app.listen(port, () => { console.log("Listening on " + port); //ここは要らないけど一応 });適当な人のgithubからパクった。
https://github.com/FTraian/node-express-postgresql-heroku-tutorial/blob/master/web.js4.gitでファイルをアップロードする
4-1.git commitまで
terminal> git init > git add . > git commit -m "first commit"4-2.公開鍵を設定
ここは僕自身理解度が低いですが、ひとまず僕が成功した手順を書いておきます。
まず、公開鍵が設定されていないことを確認します。terminal> heroku keys ! You have no SSH keys.確認できたら、ssh公開鍵を作成します。"xxx@gmail.com"の部分はherokuに登録したメールアドレスを入力します。
terminal> ssh-keygen -t rsa -C "xxx@gmail.com"すると以下のようにでてくるので、Enterキーを押したりpassphraseを設定したりします。
terminalGenerating public/private rsa key pair. Enter file in which to save the key (C:\Users\ユーザー名/.ssh/id_rsa): //そのままEnterキー Created directory 'C:\Users\ユーザー名/.ssh'. Enter passphrase (empty for no passphrase): //パスフレーズを入力(忘れないように)
その後、Herokuに登録して完了です。
terminal> heroku keys:add C:\Users\ユーザー名\.ssh\id_rsa.pub //id_rsa.pubがあるファイル名を指定 > heroku keys === xxx@gmail.com keys ...(公開鍵) xxx@gmail.comhttps://daipresents.com/2012/post-5010/
https://knowledge.sakura.ad.jp/3543/4-3.git push
初回のみ以下のコマンド。
terminal> git remote add heroku git@アプリ名.gitあとはいつもどおり。
terminal> git push origin masterここまでで、自分のアプリのURLをクリックしたら開けると思います。ありがとうございました。
おまけ
ローカル環境からDBをいじるには
terminal> heroku pg:psql -a アプリ名 アプリ名::DATABASE=>
- 投稿日:2020-06-30T19:59:52+09:00
Angular 'Tour of Heroes' でMEANスタック
始めに
前回の記事MEANスタックの一歩前。Angular と Express の連携の続きです。
Angular の公式チュートリアル、'Tour of Heroes'をMEANスタック化します。前回はAngular の公式からダウンロードした'Tour of Heroes'を若干修正して実際のサーバにアクセスするようにしました。
その後Expressサーバーを構築しました。
ですがデータベースは使用しておらず、サーバー内に配列としてヒーローのリストを持つのみとしていました。今回はWindows環境ですが、MongoDBをセットアップしてMEANの「M」の部分を完成させたいと思います。
MongoDB のインストール
まずはMongoDBのダウンロードを行います。MongoDB公式で、「Available Downloads」に必要な環境情報を登録して「Download」をクリック。インストールパッケージをダウンロードします。私が使用したパッケージファイル名は「mongodb-win32-x86_64-2012plus-4.2.7-signed.msi」でした。
ダウンロードしたら、一応ウイルスチェックを行いましょう。
公式サイトでウイルスを検出してしまうことは経験上ありませんが、念のため。続けてインストールを進めていきます。
今回は、すべてデフォルト設定を受け入れるだけなので難しい部分はありませんでした。では早速、インストールを進めていきます。
まずはインストーラをダブルクリックで起動。
Nextで次に進みます。
問題が無ければデフォルト設定のまま、Nextをクリックします。
GUI環境であるCompassを使用するため、チェックしたままNextをクリックします。
インストール作業の途中でMongoDBのGUIツールであるCompasが起動しました。
完了です。
自動で起動したCompasの画面です。
MongoDBにデータベースを登録
'Tour of Heroes'で使用するデータベースをMongoDBに登録します。
先ほどのCompasの画面中央にある緑のConnectボタンをクリックします。
画面上部の「CREATE DATABASE」をクリックします。
データベースの名前の登録を求められるので、「Database Name」と「Collection Name」に"heroes"と入力し、「CREATE DATABASE」をクリックします。
heroes のデータベースが追加されているのが分かります。
「Heroes」をクリックします。
データベースのコレクションとしても、先ほど登録したheroes が登録されているのが分かります。
さらにheroesをクリックします。
すると、データが何もないことが分かります。
ここに新しいデータを登録していきます。このタイミングでデータを登録しなくても、この後で作成する
MEANスタックの完成形から1項目ずつ登録する事で、'Tour of Heroes'の
データを再現する事もできます。
「ADD DATA」をクリックし、ドロップダウンの「Insert Document」をクリックします。
この様にJSON形式でデータを登録します。
ちなみに、数値以外の項目はすべて「"」でくくる必要があります。
チョットだけはまりポイントでした。コピペ用にテキストも張っておきます。[
{ "id": 11, "name": "Dr Nice" },
{ "id": 12, "name": "Narco" },
{ "id": 13, "name": "Bombasto" },
{ "id": 14, "name": "Celeritas" },
{ "id": 15, "name": "Magneta" },
{ "id": 16, "name": "RubberMan" },
{ "id": 17, "name": "Dynama" },
{ "id": 18, "name": "Dr IQ" },
{ "id": 19, "name": "Magma" },
{ "id": 20, "name": "Tornado" }
]
この様に、データが登録されました。Express のMongoDB 対応
まずは Node でMongoDBにアクセスするため mongoose のモジュールを追加します。
コマンドプロンプトでプロジェクトのディレクトリに移動し、D:\angular\toh-srv>npm install mongooseとして、mongooseをサーバープログラムに追加します。
次にソースファイルを修正していきます。
MongoDB に登録したデータベース対応するため、モデルを定義します。
前回の記事MEANスタックの一歩前。Angular と Express の連携のソースファイルに
model.js を追加します。toh-srv\model.jsconst mongoose = require('mongoose'); mongoose.connect("mongodb://localhost/heroes"); const HeroesModelSchema = new mongoose.Schema({ id: Number, name: String }); module.exports = mongoose.model('heroes', HeroesModelSchema);この時、
mongoose.connect("mongodb://localhost/heroes");の heroes は、MongoDBにデータベースを登録で登録した「Database Name」に対応しています。
続けて、サーバーのメインプログラムを修正していきます。
index.jsconst express = require('express'); // expressモジュールを読み込む const bodyParser = require('body-parser'); // body-parserモジュールを読み込む const multer = require('multer'); // multerモジュールを読み込む var HeroesModel = require('./model'); const app = express(); // expressアプリを生成する app.use(bodyParser.urlencoded({ extended: false })) app.use(bodyParser.json()) // heroes リストデータ /*const heroes = [ { id: 11, name: 'Dr Nice' }, { id: 12, name: 'Narco' }, { id: 13, name: 'Bombasto' }, { id: 14, name: 'Celeritas' }, { id: 15, name: 'Magneta' }, { id: 16, name: 'RubberMan' }, { id: 17, name: 'Dynama' }, { id: 18, name: 'Dr IQ' }, { id: 19, name: 'Magma' }, { id: 20, name: 'Tornado' } ]; //*/ // 新しいヒーローIDを作成. function genId(){ return new Promise(function (resolve, reject){ HeroesModel.find().sort({id:-1}).limit(1).exec({}, function(err, heroes){ if(err) return handleError(err); if(heroes.length>0) resolve(heroes[0].id + 1); else resolve(11); }); }); } // ヒーローのリストを取得 app.get('/heroes', (req, res) => { console.log('@@ get heroes'); if(req.query.name) { // req.query.name を含むヒーローのリストを作成して返す. const query_name = req.query.name; HeroesModel.find( { "name" : new RegExp( ".*"+query_name+".*") } ).select('id name').exec({}, function(err, heroes){ if(err) return handleError(err); res.json(heroes); }); }else{ HeroesModel.find().select('id name').exec({}, function(err, heroes){ if(err) return handleError(err); res.json(heroes); }); } }); // idを指定してヒーローを取得 app.get('/heroes/:id', (req, res) => { const query_id = req.params.id; console.log('@@ get heroes id:' + query_id); HeroesModel.find( { "id" : query_id } ).select('id name').exec({}, function(err, heroes){ if(err) return handleError(err); if(heroes.length<=0)res.sendStatus(404); res.json(heroes[0]); }); }); // 新しいヒーローを登録 app.post('/heroes', (req, res) => { const query_name = req.body.name; console.log('@@post heroes :' + query_name); genId().then(function(newId){ console.log(newId); var heroes = new HeroesModel({ id: newId, name: query_name }); heroes.save(function(err){ if(err) return handleError(err); // 追加した項目をクライアントに返す res.json(heroes); }); }).catch(function (error) { // 非同期処理失敗。呼ばれない console.log('catch err :' ); console.log(error); }); }); // ヒーロー削除 app.delete('/heroes/:id', (req, res) => { const query_id = req.params.id; console.log('@@delete heroes id:' + query_id); HeroesModel.remove( { "id" : query_id }, function(err){ if(err) return handleError(err); // ステータスコード200:OKを送信 res.sendStatus(200); }); }); // ヒーロー名修正 app.put('/heroes', (req, res) => { // URLの:idと同じIDを持つ項目を検索 const query_id = req.body.id; const query_name = req.body.name; console.log('@@put heroes / req.body:' + query_id); console.log('@@put heroes / req.body:' + query_name); HeroesModel.update( { "id" : query_id }, {$set: { name: query_name}} ).select('id name').exec({}, function(err){ if(err) return handleError(err); // ステータスコード200:OKを送信 res.sendStatus(200); }); }); // ポート3000でサーバを立てる app.listen(3000, () => console.log('Listening on port 3000'));先ほど定義したモデルをインポートしています。
代わりに heroes リストデータ をコメントアウトしています。
この部分は削除して構いません。新しいヒーローIDを作成する genId 関数と併せて、
get、post、delete、put、それぞれの部分でMongoDBに対応したコードに置き換わっています。
特に post で genId を使用する部分では少し苦労しました。
Promise についての理解がなかったため MDNのサイトExpress Web フレームワーク (Node.js/JavaScript)で基本から学び直しました。テスト
さて、これで完了です。
あとはサーバーとクライアントを双方起動させれば'Tour of Heroes'の元の状態を再現出来ていると思います。終わりに
一応 MEANスタックと呼べる状態には出来たと思います。
しかし、本格的なWebアプリを構築しようとするとまだまだやるべき事があるように思います。
ここから何かしらの改造をできるかどうか・・・モチベーション次第かと(^^ゞリンク
MDN web docs。 Express だけではなく、Web系の開発のイロハが盛りだくさんでした。
Express Web フレームワーク (Node.js/JavaScript)
- 投稿日:2020-06-30T10:23:11+09:00
どうしてもKubernetesを使う気になれず、ECSを使ってみる
ECSが好き
単純にdocker-composeの延長で使えてfargateにしておけばだいたい安定しているイメージ
運用に必要な知識が少ないし、簡単に作れるしというところでいつもECSを使うことが多いです。準備
今回はECS上にnginxとnode.jsのコンテナを作って、EFSをマウントするところまでを実施します。
載せるのは簡単なVueプロジェクト+APIなので基本の基本としてメモしておきます。クラスターの作成
まずはECSクラスタを作成します。
一瞬でできあがるのが素晴らしいですね。
クラスター名などは適当に変えてください。そうするとクラスターが作成されます。
一瞬だったでしょう?
次はdockerでいうdoccker-compose相当になるタスク定義などを作成していきます。
- 投稿日:2020-06-30T08:52:56+09:00
docker-composeでNode.js(Express)とPostgresSQLを連携したサービスを作る勉強会
概要
先日行われた勉強会にて簡易ではありますが表題のサービスを作りました。Dockerの勉強の延長で取り組みました。この記事ではそのときの勉強会の内容を記載します。
どんなモノをつくるのか?
- チャットのようなアプリケーション
- docker-composeによるデータベースとの連携
- Node.js(Express)でフロントエンド&バックエンド
- データベースはPostgresSQL
完成したソースコード
https://gitlab.com/tamoco-mocomoco/study-docker-compose
参考にさせていただいた記事
https://qiita.com/ryo-ohnishi/items/b54e649b14b51694ef77
作ったモノの説明
docker-compose
サービス:app
app: build: context: ./ # Dockerfile保存場所 depends_on: - database image: n-app # イメージ名 container_name: n-app # コンテナ名 ports: # ポート接続 - 3000:5000 environment: PORT: 5000 DB_USER: postgres DB_HOST: database DB_PORT: 5432 DB_NAME: test_db
app
はNode.js(Express)
がメインとなるサービスになります。ここでは学習のために敢えてデフォルトのポートを5000
に変更しています。これは参考にさせていただいたExpressの環境変数でPORT
が使用されており、それをdocker-compose
で5000
に上書きしています。コンテナ内の5000
をホスト側の3000
に転送しています。ちなみにExpress
で環境変数が使用されている箇所はapp/bin/
のwww
というファイルで確認できます。app/bin/www
#!/usr/bin/env node /** * Module dependencies. */ var app = require('../app'); var debug = require('debug')('myapp:server'); var http = require('http'); /** * Get port from environment and store in Express. */ var port = normalizePort(process.env.PORT || '3000'); app.set('port', port); ~以下省略~サービス:database
database: image: postgres:12.3 volumes: - ./init-sql:/docker-entrypoint-initdb.d:ro environment: POSTGRES_DB: test_db TZ: "Asia/Tokyo" POSTGRES_HOST_AUTH_METHOD: trust
database
はPostgresSQL
を使用しています。docker-compose
で環境変数であるPOSTGRES_DB
をtest_db
に設定することで自動的にtest_db
という名前のデータベースが作成されます。
volumes
の設定はinit-db
というフォルダにSQLファイルを格納しておくことで、SQLファイルに書かれているクエリを自動的に実行してくれます。
初期スキーマの設定(テーブル構造の作成)やダミーデータなどを挿入するときに利用します。実行順序はファイル名の昇順で実行されるので、ファイル名に番号などをつけるとよさそうです。
今回、作成したデータベースには
postgres
というユーザーが作成されますが、パスワードが設定されていません。そのためPOSTGRES_HOST_AUTH_METHOD
をtrust
に設定する必要があります。あと、
TZ
でタイムゾーンを設定しています。タイムゾーンの変更については以下のクエリでも可能です。ALTER DATABASE test_db SET timezone TO 'Asia/Tokyo'; SELECT pg_reload_conf();
init-db
にクエリをファイルとして格納しておけばタイムゾーンも変更可能です。ただし、どこでタイムゾーンが設定されているか後追いしづらいので、環境変数で設定できるなら環境変数を使った方が良いと思います。Dockerfile:Node.js(Express)
今回のアプリケーションの部分(サービス:app)は
app
フォルダ配下
にExpress
のソースが格納されています。それらのソースを元にDockerfile
でコンテナを作成します。Dockerfile
# ベースイメージを指定 FROM node:10.12 # 環境変数設定 ENV NODE_ENV="development" # 作業ディレクトリ作成&設定 WORKDIR /src COPY ./app /src RUN npm install CMD npm run startここで重要なのでコンテナの中に
src
というフォルダを作成し、そこにホスト(自分の端末)のapp
フォルダの中身をコピーします。src
はWORKDIR
に設定されているのでsrc
でコピーしたソースファイルを元にnpm install
などを実行できます。今回は
Dockerfile
でソースをコピーしましたがdocker-compose
でも同じようなことが可能です。Node.js(Express)
ここは別記事にしてもよいくらい説明する箇所が多いので抜粋して記載します。今回の
Express
のソースではapp/routes
フォルダのファイルでエンドポイントを設定して、表示内容をapp/views
フォルダのテンプレートのファイルに渡してコンテンツを表示する流れになっています。処理の流れ app/routes → app/viewslocalhost:3000 にアクセス
app/routes/index.js ↓ app/views/index.ejs
index.js
ではPostgresSQL
のchat
テーブルをセレクトした結果をindex.ejs
に渡しています。localhost:3000/insert?msg=SEND_TEST
app/routes/insert.js ↓ app/views/index.ejs
insert.js
ではURLパラメーターでmsg
を受け取ることができます。msg
の値をchat
テーブルにインサートします。もしパラメーターがない場合は「ふにょふにょ」という値がインサートされます。表示する内容はindex.ejsと同じですがインサートされた後のテーブルの情報を表示します。
app/routes
のファイルとapp/views
のファイルは必ず1対1にする必要はなく、同じテンプレートでも渡す内容で表示内容を切り替えるなどの制御も可能だったりします。データベース(PostgresSQL)への接続:db_client.js
module.exports = { pg_client: function () { const { Client } = require('pg') const client = new Client({ user: process.env.DB_USER, host: process.env.DB_HOST, port: process.env.DB_PORT, database: process.env.DB_NAME }) return client }, pupupu: function () { console.log('pupupupu') } };
db_client.js
でデータベースの接続処理を共通で使用できるようにしています。PostgresSQL
に必要なユーザー情報などはdocker-compose
から環境変数で受け取りそれを元に接続を行います。総評
ある程度コンパクトにまとまっており、構成もそこまで複雑ではないかなと思いました。ただ開発するときに少し難があり
Express
は個別でnpm install
などでpackage.json
などを更新する必要があります。これはdocker-compose build
はapp
フォルダをコピーしているので、アプリのnode_module
などは手動で更新しないとコンテナで動作してくれません。逆にnpm start
などで開発すればよい話になりますが、データベースとの接続ができないので困ったところです。
しかしながら、今回の勉強会ではいろいろとdocker-compose
について理解を深めることができたのでよかったかなと思います。
- 投稿日:2020-06-30T02:45:39+09:00
秋月電子で買ったLEDクラスターランプをobnizで試したメモ #obniz
少し前に秋月電子の店先で見つけて 30円という安さもあり試しに買ったLEDクラスターランプを触ってみました。
LEDクラスターランプ
店先に置いてあって面白そうだったので購入してみました。
緑と赤のランプです。
データシートを読んでみた
データシート読んだ仕様などはこちらにメモしてます。
obnizから利用してみた
電気を流すかGNDにするかの指定だけみたいだったので一旦これで試してみました。
'use strict' const Obniz = require('obniz'); const obniz = new Obniz(process.env.OBNIZID); obniz.onconnect = async function () { obniz.io0.output(false); //ケーブル赤 io0 - obniz.io1.output(true); //ケーブル白 io1 + obniz.io2.output(false); //ケーブル緑 io2 - }$ OBNIZID=xxxx node led.js特に問題なく起動
全てのLEDが点灯しました。
遊んでみた
LED cluster lamp + obniz pic.twitter.com/SFgf7dh7dY
— 菅原のびすけ (@n0bisuke) June 29, 2020この辺のコードもこちらに。
けっこう楽しい。1分くらいは見てられる()
- 投稿日:2020-06-30T01:19:48+09:00
Python Node.js 文字操作
Python, Node.jsともに以下の順で変換
16進数文字列 ⇔ バイナリ ⇔ 文字列 ⇔ Unicode(10進数) ⇔ 16進数文字列
10進数(int型) ⇔ 2進数(str型)、8進数(str型)
Base64 ⇔ バイナリ ⇔ urlエンコードそして、Python, Node.jsどちらもデフォルトのエンコーディングはUTF-8です。
Python
#-*- encoding:utf-8 -*- import base64 import import urllib.parse str_data = "あ" encoded = str_data.encode() #b'\xe3\x81\x82' hex_str = encoded.hex() #"e38182" encoded = bytes.fromhex(hex_str) #b'\xe3\x81\x82' base64_str = base64.b64encode(encoded) #b'44GC' encoded = base64.b64decode(base64_str) #b'\xe3\x81\x82' str_data = encoded.decode() #"あ" urlencoded = urllib.parse.quote(str_data) #'%E3%81%82' str_data = urllib.parse.quote(urlencoded) #"あ" unicode = ord(str_data) #12354 hex_str = hex(unicode) #'0x3042' unicode = int(hex_str,16) #12354 binary = bin(unicode) #'0b11000001000010' unicode = int(binary,2) #12354 oct_str = oct(unicode) #'0o30102' unicode = int(oct_str,8) #12354 str_data = chr(unicode) #"あ"Node.js
let strData = "あ"; let encoded = Buffer.from(strData); //<Buffer e3 81 82> let hexStr = encoded.toString("hex"); //"e38182" encoded = Buffer.from(hexStr,"hex"); //<Buffer e3 81 82> let base64Str = Buffer.from("あ").toString("base64"); //'44GC' encoded = Buffer.from(base64Str,"base64") //<Buffer e3 81 82> strData = Buffer.from(encoded).toString(); //"あ" urlencoded = encodeURIComponent(strData) //'%E3%81%82' strData = decodeURIComponent(urlencoded); //"あ" let unicode = strData.codePointAt(0); //12354 let hexStr = unicode.toString(16); //'3042' unicode = parseInt(hexStr,16); //12354 let binary = unicode.toString(2); //'11000001000010' unicode = parseInt(binary,2) //12354 let octStr = unicode.toString(8); //'30102' unicode = parseInt(octStr,8) //12354 strData = String.fromCodePoint(unicode); //"あ"
- 投稿日:2020-06-30T01:16:50+09:00
S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする
S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする
はじめに
前回のS3に保存されたwavファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしするに引き続き、
今回はMP3ファイルを対象に、S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする手順をまとめます。重複する点が非常に多いので、相違点になるコードの部分だけ(前回でいう5-9のみ)ご紹介します。
Code
実行するコードは以下になります。
const AWS = require('aws-sdk'); const speech = require('@google-cloud/speech').v1p1beta1; const client = new speech.SpeechClient(); const s3 = new AWS.S3({ apiVersion: '2012-09-25' }); exports.handler = function(event, context) { const bucket = event.Records[0].s3.bucket.name; const key = event.Records[0].s3.object.key; const params = { Bucket: bucket, Key: key }; s3.getObject(params, async (err, data) => { if (err) { console.log(err, err.stack); } else { const audioBytes = data.Body.toString('base64'); const audio = { content: audioBytes }; const config = { encoding: 'MP3', sampleRateHertz: 44100, languageCode: 'ja-JP', }; const request = { audio: audio, config: config, }; const [response] = await client.recognize(request); const transcription = response.results.map(result => result.alternatives[0].transcript).join('\n'); console.log(transcription); }; }); }ポイント
1. Cloud Speech-to-TextをMPx3で使うにはβ版を使う
音声エンコードの概要に記されている通り、MP3ファイルの場合はベータ版のみが使用できます。 なので2行目に変更があります。
const speech = require('@google-cloud/speech').v1p1beta1;2. async/awaitで非同期処理対策
認識をしているときに、非同期で処理が行われてしまいうまく文字起こしできない可能性があるので、前回は入れなかったasync/awaitを入れてその対策をしました
const [response] = await client.recognize(request);3. sampleRateHertzの調整
お好みのツールを使ってsampleRateHertzを確認して調整していただければと。もしかしらこのパラメータ設定しなくても行けるかもしれません。
結果
transcriptionの中に入っています。個人的には結構な認識精度で驚いています。
最後に
今回は、前回の記事に続いて、MP3のデータを扱ってみました。
正直、JavaScript、Node.jsを雰囲気でやってる人間なので間違いなどあったらぜひ教えていただきたいです。参考