XXとしてる
2019年3月31日日曜日
Google App Engineにデプロイしたときの話
ようやくWebサイトを公開してもいい感じになったのでGoogle App Engineでデプロイしようとした。 ## さぁ、デプロイだ! ```bash gcloud app deploy gcloud app browse ``` <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" data-original-width="60" data-original-height="105" /></a></div> <div style="text-align:center;">? 画面が真っ白だぞ??</div> ログを見ると何やら失敗しているっぽい。 ## npmで試す ドキュメントの方はnpmを使っているので、いったんyarnを使うのやめてnpmを使って試してみる。 が、ダメ。 ログを見ると ``` Error: Cannot find module '<install packages>' ``` とあった。 yarnの方にもそういえば同じログがあったので、パッケージのインポート周りで失敗しているっぽい? ## もしかして? devDependenciesしてます? してまーす。 というわけで、慌ててdependenciesにインストールし直す。 が、ダメ。 ``` 0x6d2937abc60 node::Abort() [node] ``` と出て、闇がさらに深くなってしまった。 ## デフォルトのnuxtで試してみる 何がおかしいのかさっぱりなので、Nuxtを初期化した状態のものでデプロイが出来るか確認してみる。 npmを使い、`npm create nuxt-app`で現在のプロジェクトに近い設定で初期化。 設定した項目は - Express - Vutify - PWA などなど ちゃんと`npm run build`をしてからデプロイを実行! へへ、これは流石に動くだろう。 ``` Error: Server Error The server encountered an error and could not complete your request. Please try again in 30 seconds. ``` 何も変わらない・・・だと・・・ ## 助けて、Qiita!! ポートとホスト名をそれぞれ8080、0.0.0.0と環境変数で指定してあげるといいんだよ! ありがとう!Qiita!! ありがとう!記事を書いてくれた人!! ``` Error: Server Error The server encountered an error and could not complete your request. Please try again in 30 seconds. ``` なん・・・だ・・と・・・・ ## まだだ!まだまっさらな状態のNuxtが残っている。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnS6Tp9am9LL9iwSAiTkIV715EGk2RXd16r7moM4aQcHdTfzklOqY1jltFp0aRqCVX6wSbNW3waCaLHO2mrR5Mf_10nzLxjPRZ8r93LKpnimLBHSBWaqqR08kbICnwfm-rCJiYMPXYOxs/s1600/Screenshot_2019-03-31+default.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnS6Tp9am9LL9iwSAiTkIV715EGk2RXd16r7moM4aQcHdTfzklOqY1jltFp0aRqCVX6wSbNW3waCaLHO2mrR5Mf_10nzLxjPRZ8r93LKpnimLBHSBWaqqR08kbICnwfm-rCJiYMPXYOxs/s320/Screenshot_2019-03-31+default.png" width="320" height="301" data-original-width="510" data-original-height="480" /></a></div> <div style="text-align:center;">やったー!動いた!!</div> ## 何が違うの? どうやらnuxt buildをしているかしていないかが失敗の原因っぽい。 サーバー側ではExpressを使用しているが、そのときのnpm run startはnode経由でサーバーを立ち上げている。 なにか違いがあるのか、nuxtのコードを読んでみたが、次のコードでNuxtインスタンスを生成していた。 ```js //node_modules/@nuxt/cli/dist/cli-chunk.js async getNuxt(options) { const { Nuxt } = await core(); const nuxt = new Nuxt(options); await nuxt.ready(); return nuxt } ``` ちなみにnuxt startの実行したときに処理されるコードはここになると思う。 ```js //node_modules/@nuxt/cli/dist/cli-chunk4.js async run(cmd) { const config = await cmd.getNuxtConfig({ dev: false, _start: true }); const nuxt = await cmd.getNuxt(config); // Listen and show ready banner await nuxt.server.listen(); __chunk_2.showBanner(nuxt); } ``` これと生成時にExpressを使うように設定したときのサーバーコードを比較してみると、特に違った箇所が見受けられなかった。 両者ともNuxtインスタンスを作成してリクエスト待ちを行っている。 違うといえば、Expressの方はexpressにnuxt.renderを渡しているという点か… ```js async function start() { // Init Nuxt.js const nuxt = new Nuxt(config) const { host, port } = nuxt.options.server // Build only in dev mode if (config.dev) { const builder = new Builder(nuxt) await builder.build() } else { await nuxt.ready() } // Give nuxt middleware to express app.use(nuxt.render) // Listen the server app.listen(port, host) consola.ready({ message: `Server listening on http://${host}:${port}`, badge: true }) } ``` ## printfデバッグしてみる printfデバッグはつらい。が、やるしかない。Google App Engineのログに残ってくれたらいいのだが・・・ (※ローカル環境で試すだけで十分です) ちなみにこんな感じにログを差し込んだ ```js async function start() { // Init Nuxt.js const nuxt = new Nuxt(config) console.log('通ったよ') const { host, port } = nuxt.options.server // Build only in dev mode if (config.dev) { const builder = new Builder(nuxt) await builder.build() } else { await nuxt.ready() } console.log('通ったよ1') // Give nuxt middleware to express app.use(nuxt.render) console.log('通ったよ2') // Listen the server app.listen(port, host) consola.ready({ message: `Server listening on http://${host}:${port}`, badge: true }) console.log('通ったよ3') } ``` 実行結果は・・・ ``` I GET 500 0 B 8.8 s Firefox 66 / GET 500 0 B 8.8 s Firefox 66 5ca0b63f00ff066b499bee7a370001627e6b616e6a692d617373656d626c792d3233363230380001323031393033333174323134333230000100 A 通ったよ // 省略 A 1: 0x7779f97ec60 node::Abort() [node] ``` と、真ん中あたりの条件分岐が怪しいことがわかった。 これを繰り返していくと、await nuxt.ready()で失敗していることがわかった。 nuxt.ready()内部に問題があるというわけではなく、Nuxtインスタンスを生成するときに渡した設定が原因ではないかと思う。 というわけで比較してみると、buildオプションが大きく異なっていることがわかった 試しに、Expressだけ有効にしてほかは何も使わない設定でNuxtプロジェクトを作成すると無事デプロイに成功した! ## 悪いビルドオプションは誰だぁ!! 「誰もいません!!」 「じゃ、誰だ悪さをしているんだ!!」 「こいつです!!!」 <div style="font-size: 64px;"><b>Progressive Web Apps</b></div> 正しくはmodulesの@nuxtjs/pwa。 原因はわからないが、とりあえずこいつが原因だとわかった。 それを使わない状態でyarnを使っても、問題なくデプロイ出来たので、@nuxtjs/pwaが悪い。 以上、原因究明終わり。 ## 終わりに 検証のため幾度もなくデプロイを繰り返したが、デプロイする際にはGoogle Cloud Buildを利用する必要があり、それは有料サービスである。 つまり、お金がかかるということである。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align:center;">まずいのではないですか?</div> が一日120ビルド分までは無料だし、それを超えても$0.003/ビルド分と無いに等しいので実質無料である。 一年間$30まで無料な体験版もあり、Google App Engineは試しやすいIaaS環境だと思う。 ※ ビルド分 ビルドにかかった時間を分で割った単位
(画像は以下のものを使用させていただきました。)
2019年3月29日金曜日
パスワードの保存をしようとしてブラウザ機能を利用できなかったときの話
今作っているWebサイトで色々なサイトでよくあるパスワードを保存する機能を実装してみようかと調べてみたら、 formタグ内のボタンのtypeをsubmitにしてあげるといいとあったので早速試してみた。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrU9fN4DGRFJWrucOGBGcqHthNKmB2CRF0QLtjxZq8cbID5NbQYEhNCP3Wgl-3VYq-iPSSwVNxbKABWpXmgj12I6wSdj8CtPavewEsPxNwZt-Rt-pyv9_G4OQVd6MDgKseJ2i9ugcerjY/s1600/achieve.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrU9fN4DGRFJWrucOGBGcqHthNKmB2CRF0QLtjxZq8cbID5NbQYEhNCP3Wgl-3VYq-iPSSwVNxbKABWpXmgj12I6wSdj8CtPavewEsPxNwZt-Rt-pyv9_G4OQVd6MDgKseJ2i9ugcerjY/s1600/achieve.png" data-original-width="107" data-original-height="189" /></a></div> <div style="text-align:center;">おっ、できたぞ!簡単じゃん!</div> と思っていたら、ログインが何故かできない。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" data-original-width="60" data-original-height="105" /></a></div> <div style="text-align:center;">もしかして、formタグのアクションが実行されている?</div> 既にある処理が眠っていたFormタグのアクションで上書きされている?ような感じで、うまく動作してくれない状態になってしまった。 ## 普通はformタグのアクションとリダイレクトで作っているはずが・・・ 実のところformタグとリダイレクトで認証機能を作っていたら、とっくの前にこの問題は解決、いや問題すらなかったのだが、 <b><i>なぜか</i></b>、自分はAjaxで認証機能を作っていたのでブラウザの持つ機能を使えない状態になっていた。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align:center;">何しているんだっ!</div> いや<a href="https://ja.nuxtjs.org/examples/auth-external-jwt">Nuxt.jsのサンプル</a>とか、以前<a href="https://tositeru.blogspot.com/2019/03/authenticate-nodejs-api-with-json-web.html">Blogに書いた参考サイト</a>とかだと特にそんなこと書いていなかったので・・・、 てっきりそういうものかと思って作っていました… ## 自前でパスワード管理をすることに 便利な機能が使えなかったら自分で作るしかないじゃんと、ブラウザ側で記録して、毎回参照する形で妥協して対応した。 ちょっと背伸びした結果がこれなので、次はたくさんのひとが使っていて、型が扱えて、便利なフレームワークで作ってみたいと思う。
(画像は以下のものを使用させていただきました。)
2019年3月28日木曜日
SequelizeのバージョンをV5に上げたときの話
SequelizeのバージョンがV5に上がっていたのでアップグレートしたときの話をつらつらと書いていく。 ## yarn upgrade そもそもアップグレートすることが初体験なので、パッケージ管理ツールの使い方も知らない状態だった。 とりあえず、以下のコマンドでアップグレードした。 ```bash yarn upgrade sequelise@^5.2.1 ``` が、以下のようでもいい。 ```bash yarn upgrade sequelize --latest ``` 上のコマンドで最新のバージョンが使われるようになる(--latest)。 はじめのものはバージョン指定したもので、`^`(caret)はメジャーバージョンを変えない範囲でアップグレードをすることを示している。 要は、@^3.1.2なら3.1.2<= 〜 <4.0.0範囲のバージョンが使われるとのこと [yarn caret-ranges](https://yarnpkg.com/lang/en/docs/dependency-versions/#toc-caret-ranges) これで無事アップグレートができた ## DeprecationWarning: A boolean value was passed to options.operatorsAliases. This is a no-op with v5 and should be removed. SequelizeV5からSequelizeインスタンスを作成するときに比較演算などの演算子には必ず別名をつけることを推奨するようになっている。 これはセキュリティ的な問題で実際に使用する演算子だけをインスタンス生成時に別名として指定することで、攻撃者が好き勝手な演算子を使えなくする効果がある。 ```js //アプリで使っている演算子だけを別名定義する const usedOperatorsAliases = { $and: Sequelize.Op.and, $or: Sequelize.Op.or, } const sequelize = new Sequelize('database', { operatorsAliases: usedOperatorsAliases }) const model = Sequelize.define(...) model.findOne({ where: { $or: ... $and: ... } }) ``` いちいち定義するのがめんどくさいなら、[公式ドキュメントのOperators security](http://docs.sequelizejs.com/manual/querying.html#operators)からコピペするといい Sequelize CLIの方はまだ対応していないみたいなのでこの警告が出ている。 だけど開発用に使用するだけなので、全ての演算子の別名を指定する形で問題はないかと思う。 ## 終わり <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrU9fN4DGRFJWrucOGBGcqHthNKmB2CRF0QLtjxZq8cbID5NbQYEhNCP3Wgl-3VYq-iPSSwVNxbKABWpXmgj12I6wSdj8CtPavewEsPxNwZt-Rt-pyv9_G4OQVd6MDgKseJ2i9ugcerjY/s1600/achieve.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrU9fN4DGRFJWrucOGBGcqHthNKmB2CRF0QLtjxZq8cbID5NbQYEhNCP3Wgl-3VYq-iPSSwVNxbKABWpXmgj12I6wSdj8CtPavewEsPxNwZt-Rt-pyv9_G4OQVd6MDgKseJ2i9ugcerjY/s1600/achieve.png" data-original-width="107" data-original-height="189" /></a></div> <div style="text-align: center;">アップグレート完了!</div> 思いの外、修正する部分が少なく助かった
(画像は以下のものを使用させていただきました。)
2019年3月24日日曜日
Gitの衝突とマージ
今作っているものはGitでmasterとdevelopでブランチを分けて作業している。 なのであるタイミングでdevelopの内容をmasterへマージするのだが、マージコンフリクトが起きてしまった。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align: center;">またかよっ!</div> そもそも一人で作っているのになんでmasterとdevelopでコンフリクトが起きるんだよっと思ったら、 うっかりマスターに変なコミットしてしまって、git revertで打ち消していたことを思いだした。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwTDeWjQayVVpdoD-bSWF7fTcWIQNqWRHyOnRLv8qETX9-voJ7hD_aF2Gz-cdT-6USIIUcanWP3xgknbHq_g0bDzpwhZ971uEgCKUl_Hvrz3lN-WTv8BX8bbxAhNROt_Lh9_5mkVthWpg/s1600/lol.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwTDeWjQayVVpdoD-bSWF7fTcWIQNqWRHyOnRLv8qETX9-voJ7hD_aF2Gz-cdT-6USIIUcanWP3xgknbHq_g0bDzpwhZ971uEgCKUl_Hvrz3lN-WTv8BX8bbxAhNROt_Lh9_5mkVthWpg/s1600/lol.png" data-original-width="128" data-original-height="142" /></a></div> <div style="text-align: center;">ハハッ、やっちまったな!</div> <div style="text-align: center; font-size: 32px;">.<br>.<br>.<br>.<br>.<br>.</div> <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF22uBZBdA63ptQUblnFWQJ1WRoOfhg3xx7uUuIhYH8M97SELnTnQ8dZYAblq02RL_YVV91rwH22Nn3eIKe1K5EF56vefY_B6bdtBanRZAhq4T5kURgr7SBSVmNJcHpEI0S7GV_MUnlzI/s1600/be+attacked.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF22uBZBdA63ptQUblnFWQJ1WRoOfhg3xx7uUuIhYH8M97SELnTnQ8dZYAblq02RL_YVV91rwH22Nn3eIKe1K5EF56vefY_B6bdtBanRZAhq4T5kURgr7SBSVmNJcHpEI0S7GV_MUnlzI/s1600/be+attacked.png" data-original-width="225" data-original-height="156" /></a></div> git reset --hardで強制的に消しても良かったけど、なんとなくそのままにしておいたツケが回ってきた感じである。 ## rebaseよりmerge こういった問題に直面したとき自分はgit rebaseとgit push -fを使っていたが、 コミットログが消えてしまうのは困ることに気がついてからマージでなんとかしようと心がけている。 がまだまだ初心者。[ドキュメントを見ながらの作業](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E9%AB%98%E5%BA%A6%E3%81%AA%E3%83%9E%E3%83%BC%E3%82%B8%E6%89%8B%E6%B3%95#r_advanced_merging)になるので、心の中ではコンフリクトは起きないでくれと願っている。 基本的にmaster側でどうにかするのではなくて、develop側でコンフリクトを解消する方針を立てている。 今回もそれに従って、masterにある変更点をdevelopにマージすることでコンフリクトを解消し、それをmasterにマージする流れになる。 なんだか二度手間ではあるが、OSS的にはそっちのほうがいいとかないとか ただ、今回は何も考えなくマージするとdevelop側のファイルが消えてしまう状態になってしまった。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" data-original-width="60" data-original-height="105" /></a></div> <div style="text-align: center;">うわー出たよ、Git使っていて困るパターンだ…</div> ## git merge -Xours \<branch\> ドキュメントにはコンフリクトがある場合どちらか一方の変更を取り込むよう指定できるオプションについて書かれていたので、まずそちらを試してみた。 ```bash git checkout develop # コンフリクトがあったらdevelopの方を採用する git merge -Xours master # こっちだとmasterの変更点が採用される git merge -Xtheirs master ``` が、ダメ。 そもそもコンフリクトが起きていなかったので、これではダメである。 ## git merge -s ours \<branch\> ドキュメントのその次の説明ではGitを騙して何もマージしていいないのにマージしたことにするオプションを紹介していたのでそちらを試してみた。 ちょうど、master/developブランチで開発をしている想定での説明だったのでこれはいいのではないかと期待を抱いてタイピングする。 ```bash git checkout develop #マージをしたつもりマージ developの内容そのままで保持される git merge -s ours master ``` <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSKIxJpYiqBTqd4LvVKPjCC_BG990xDE7Cp67Q7PYrQBy7w7uuz6mvHCIh7vuTWQrQ5_ijFrLLKXUOyy0weJ7drqGegTWytTYIZEJ71qV0i1kPLEiDJqIWezCKsIUIGWxnwCtaRYi-RC4/s1600/Screenshot+from+2019-03-23+20-26-20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSKIxJpYiqBTqd4LvVKPjCC_BG990xDE7Cp67Q7PYrQBy7w7uuz6mvHCIh7vuTWQrQ5_ijFrLLKXUOyy0weJ7drqGegTWytTYIZEJ71qV0i1kPLEiDJqIWezCKsIUIGWxnwCtaRYi-RC4/s320/Screenshot+from+2019-03-23+20-26-20.png" width="320" height="92" data-original-width="771" data-original-height="221" /></a></div> <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrU9fN4DGRFJWrucOGBGcqHthNKmB2CRF0QLtjxZq8cbID5NbQYEhNCP3Wgl-3VYq-iPSSwVNxbKABWpXmgj12I6wSdj8CtPavewEsPxNwZt-Rt-pyv9_G4OQVd6MDgKseJ2i9ugcerjY/s1600/achieve.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrU9fN4DGRFJWrucOGBGcqHthNKmB2CRF0QLtjxZq8cbID5NbQYEhNCP3Wgl-3VYq-iPSSwVNxbKABWpXmgj12I6wSdj8CtPavewEsPxNwZt-Rt-pyv9_G4OQVd6MDgKseJ2i9ugcerjY/s1600/achieve.png" data-original-width="107" data-original-height="189" /></a></div> <div style="text-align: center;">やったぜ!</div> ミスした原因お前じゃないかとは思うが、Gitを使っていたときに問題に直面して解決したときは心の中ではこんな感じで一喜一憂な起伏が生じてしまう。 やったぜ! 上のコマンドはもともとdevelopブランチにバグ修正用のブランチをマージしたときに上のコマンドでmasterにもバグ修正用ブランチをマージすることで、 将来developをmasterにマージしたときにコンフリクトが発生させないために使うものだそうだ。 いつでも使えるコマンドではないが、今回のケースではちょうどいい動作であったので採用し問題を解決できた。 解決できてよかった!
(画像は以下のものを使用させていただきました。)
2019年3月23日土曜日
requireとimport
サーバーサイドとクライアントサイドでモジュールのインポート/エクスポートを異なる書き方をしていた。 サーバーサイドはrequireを、クライアント側はimportを使ってコーディングを行っていた。 まぁ、ちょうどどちらのサイドを書いているのか区別がつくからまぁいいかと思っていたところ、 サーバーサイドのコードをクライアントでも使いたい場面が出てきて、何も考えずサーバーサイドのコードなのでrequireでインポートしたところエラーが出てきた。 ```js import Hoge from './hoge' import Foo from '../Foo' // error!! const WantToUseModule = require('../server/module') ``` エラーが起きた場所はインポートしたいモジュールの中で、`"exports" is read-only`とエラーメッセージを表示していた。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align: center;">割と困る</div> ブラウザ環境などクライアント側では一部でしかimport構文をサポートしていないため、このようなエラーが起きた。と思う。 ## サーバー側での対応 開発環境ではNodejsのバージョン10を使用しているため、完全ではないがES6のモジュール機能に対応している。 なので、サーバーサイドでもそれを使うことでエラーはなくなり、正常に動作した。 <div style="text-align: center;font-size:32px">.<br>.<br>.</div> <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNbVBFEMzh3sqOUqeGpvvlI4ZVfW-r-30_30nT5GlxaFpgCYpPs_kRPoLuTV_A-IhPfHWZGRN-OmH0qq4XMVrb8WvVImynolv-Dx8R9lcdgVTNwr3-2SIaJcc0aLMPmvYOhGqwUA8Hc3A/s1600/be+attacked.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNbVBFEMzh3sqOUqeGpvvlI4ZVfW-r-30_30nT5GlxaFpgCYpPs_kRPoLuTV_A-IhPfHWZGRN-OmH0qq4XMVrb8WvVImynolv-Dx8R9lcdgVTNwr3-2SIaJcc0aLMPmvYOhGqwUA8Hc3A/s1600/be+attacked.png" data-original-width="225" data-original-height="156" /></a></div> <div style="text-align: center;font-size:32px">はじめから使っとけ!!</div> そのとおりです。何も言えないです。
(画像は以下のものを使用させていただきました。)
2019年3月22日金曜日
動的インポートとJSでのポインタ渡し
webpackとOpenLayerを使うと大体こんな感じのコードになると思う。 ```js import Feature from 'ol/Feature.js'; import Geolocation from 'ol/Geolocation.js'; import Point from 'ol/geom/Point.js'; import {Draw, Modify, Snap} from 'ol/interaction.js'; import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js'; import Map from 'ol/Map.js'; import {OSM, Vector as VectorSource} from 'ol/source.js'; import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style.js'; import View from 'ol/View.js'; // なんらかの処理 ``` <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" data-original-width="60" data-original-height="105" /></a></div> <div style="text-align:center;">毎回このインポート書くの面倒だなぁ・・・</div> というわけで、いわゆるc言語での必要なヘッダーをすべてインクルードした`all.h`みたいなものを作ってみた。 さらにES6では動的インポートというものも導入されたそうなのでそれも使ってみる。 これで不要なものはインポートしなくて済むから効率的だし一つのファイルをインポートするだけで使えるし、よかったよかった。 ```js // olDynamic.js All.h的なもの export default { Map: () => import(/* webpackChunkName: "ol/Map" */ 'ol/Map'), View: () => import(/* webpackChunkName: "ol/View" */ 'ol/View'), Layer: () => import(/* webpackChunkName: "ol/layer" */ 'ol/layer'), source: () => import(/* webpackChunkName: "ol/source" */ 'ol/source'), Feature: () => import(/* webpackChunkName: "ol/Feature" */ 'ol/Feature'), Geolocation: () => import(/* webpackChunkName: "ol/Geolocation" */ 'ol/Geolocation'), interaction: () => import(/* webpackChunkName: "ol/interaction" */ 'ol/interaction'), style: () => import(/* webpackChunkName: "ol/style" */ 'ol/style'), geom: { Point: () => import(/* webpackChunkName: "ol/geom/Point" */ 'ol/geom/Point') } } ``` ```js // 使う側 import olDynamic from './olDynamic' const Map = await olDynamic.Map() const Feature = await olDynamic.Feature() const Geolocation = await olDynamic.Geolocation() const Feature = await olDynamic.Feature() const Point = await olDynamic.geom.Point() const {Draw, Modify, Snap} = await olDynamic.interaction() const {Tile as TileLayer, Vector as VectorLayer} = await olDynamic.Layer() const {OSM, Vector as VectorSource} = await olDynamic.source() const {Circle as CircleStyle, Fill, Stroke, Style} = await olDynamic.style() const View = await olDynamic.View() // なんらかの処理 ``` <div style="text-align:center;font-size: 32px;">.<br>.<br>.</div> <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnAKEh2YMuJDGnBhHK-yFOvpK2iHMtRRgiWUjt7TnJ_k7PMvLgXi8_8yxbpOTMC_8MguqeEUZRkSqZI6zzjWU1AvyuQLzRUQEucl-O-5UOu3y5eZ0kQdEfLe0x17-zVoPDZKJ9SrwQvgc/s1600/lol.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnAKEh2YMuJDGnBhHK-yFOvpK2iHMtRRgiWUjt7TnJ_k7PMvLgXi8_8yxbpOTMC_8MguqeEUZRkSqZI6zzjWU1AvyuQLzRUQEucl-O-5UOu3y5eZ0kQdEfLe0x17-zVoPDZKJ9SrwQvgc/s1600/lol.png" data-original-width="128" data-original-height="142" /></a></div> <div style="text-align:center;">あんまり変わってなくない?</div> require()を使っているみたいでバージョンが逆戻りしている感じになっているし、毎回同期待ちしているのも効率的ではないから、悪くなっているぞ! ## 本題 という感じの経緯を辿ったが、やっぱり毎回importを書くのは面倒だし、動的インポートは使いたい。 だから、下のコードみたいに読み込めるようにした。 これだと[Quick Start](https://openlayers.org/en/latest/doc/quickstart.html)みたいに`ol.XXX`で使えるから割と理想的。 ```js import olDynamic from './js/ol-dynamic' async function main() { const ol = await olDynamic.imports(['Map', 'View', 'Layer', 'source', 'Feature', 'Geolocation', 'interaction', 'style', 'geom/Point']) // 何らかの処理 const map = new ol.Map({...}) } main() ``` `olDynamic.imports`の内容は以下のもの ```js imports = async function(list) { let importList = [] for(const l of list) { const dynamicImport = getProperty(this, l) importList.push(dynamicImport()) } const modules = await Promise.all(importList) let ol = {} for (let i=0; i<list.length; ++i) { setProperty(ol, list[i], modules[i]) } return ol } ``` 重要な部分は`Promise.all`とgetProperty/setProperty関数だ。 `Promise.all`のおかげで、はじめの失敗例みたいに一つ一つ読み込むのではなく並列的に読み込むようになった。 そして、`Promise.all`に渡すパラメータを生成するためにgetProperty関数を利用してリストを作った。 getProperty関数の内容は次のものになる ```js function getProperty(obj, path) { let prop = obj for(const p of path.split('/')) { if (!prop[p]) { throw new Error(`Access unknown property name... path=${path}`) } prop = prop[p] } return prop } ``` 最後に`Promise.all`の返り値からインポートしたモジュールのリストを作る。 setProperty関数をつかうのだが、それははじめgetProperty関数と似た感じになっていた。 しかし、実行してみるとちゃんと動いてくれない。 ```js //問題のあるsetProperty関数 function setProperty(obj, path, value) { let prop = obj for(const p of path.split('/')) { prop[p] = {} // この2行で意図しない処理になる prop = prop[p] // ... } prop = value // 意図しない処理になる } ``` <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align:center;">値が空オブジェクトになっているぞ!!</div> 上のコメントで書いてある部分が悪さをしているコードになる。 ## ポイント渡し風にするときは再帰関数を使う JSではオブジェクトは参照渡しになっているが、上のコードを書いたときC言語で言うポインタ渡しになっていることを期待していた。 が、オブジェクトに他の値を代入すると参照関係が切れてしまって、`obj`とは全く関係のない値になってしまっていた。 このような場合は再帰関数を使うと期待した通りに動作してくれる。 少し読みづらくなっているが、predがその再帰関数となる。 ```js //修正後のsetProperty関数 function setProperty(obj, path, value) { const pred = (prop, pathList, value) => { const p = pathList[0] if (pathList.length === 1) { prop[p] = value } else { if (!prop[p]) { prop[p] = {} } pathList.shift() pred(prop[p], pathList, value) } } pred(obj, path.split('/'), value) } ``` pred関数内で行っている処理の内容は修正前のsetProperty関数と変わっていないが、再帰関数にしたことによってコードはちょっと見た目が変わっている。 が読みづらいし、Blogなので見栄をはってif文を消し関数型言語っぽくしてみるとこうなる。 ```js function setProperty(obj, path, value) { const pred = (prop, pathList, value) => { const p = pathList[0] pathList.length === 1 ? prop[p] = value : pred(prop[p] ? prop[p] : prop[p] = {}, pathList.slice(1), value) } pred(obj, path.split('/'), value) } ``` こっちのほうが見やすいかどうかは別としてすっきりとした。<strike>分岐はしているのは許して</strike> ## 終わりに ```import {Map, View} from 'ol'```でいいことには作ってから気がついた。 動的インポートだから差別化はできているからよし。多分。
(画像は以下のものを使用させていただきました。)
2019年3月21日木曜日
Nuxtとクッキーと認証と
Nuxt.jsでWebサイトを作っているところ、ようやくユーザーのログイン/サインアップ処理ができたところ、ログインした状態でブラウザを閉じてもう一度開くとログイン状態が解除されていた。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align: center;">そぉんなぁ!!</div> 一体どうしたらいいんだ・・・ ## Cookieって? HTTP通信ではあるデータを使ったとか記録したという状態は持たないため、このような問題が起きたそうだ。 毎回ログインするのは結構困るのでどうにかしたいところ、Cookieというものにデータを保存するといいらしい。 CookieはHTTPリクエストのヘッダーにも含まれており、各サイトごとに専用のデータを保存するのに適しているものだ。 色んなサイトでこのサイトはクッキーを使用していますと注意書きしていたり、使用許可を要求してくるが、ユーザーを認識するのに適したものである。 ただしいわゆる個人番号的なものなので、そのサイトで行っていることが筒抜けになってしまうので最近EUでクッキー法と呼ばれる法律が定められたりと取扱がデリケートなものでもある。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz4Ug8JRDOHbQYxtm90ey5G7Ij0In35DIPWM0rv-b8WWmk5tlGoNz6auHke9nPkXsoHnBrRc-Nogq2vC7cojdk98OkAL1H-cU1OVPxGr-EY1o8yu412VNJsk6SM3RQNheU2hffPK43A-M/s1600/notice.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz4Ug8JRDOHbQYxtm90ey5G7Ij0In35DIPWM0rv-b8WWmk5tlGoNz6auHke9nPkXsoHnBrRc-Nogq2vC7cojdk98OkAL1H-cU1OVPxGr-EY1o8yu412VNJsk6SM3RQNheU2hffPK43A-M/s1600/notice.png" data-original-width="61" data-original-height="135" /></a></div> <div style="text-align: center;">公開するときプライバシーポリシーとか書かないといけないなぁ</div> クッキー自体は単なるデータの保存先なので、Cookie自体が悪いわけではない。 ## Nuxt.jsのミドルウェア そんなわけでクッキーを使ってユーザー認証用のデータを保存すれば今回の問題は解決!終わり! とは行かなかったので、<a href="https://ja.nuxtjs.org/guide/routing#%E3%83%9F%E3%83%89%E3%83%AB%E3%82%A6%E3%82%A7%E3%82%A2">Nuxt.jsのミドルウェア</a>についても書いていく Nuxt.jsのミドルウェアは各ページを読み込む前に行える前処理的なものになる。 なのでそれを使い先にユーザー認証用のデータが既にある/ない場合にアクセスできるページに制限を設けていた。 例えば、ログインしているときにログインページへまたアクセスするとユーザーページにリダイレクトするといった感じだ。 ページ上でログインしたあとならこれで正常に動作していたのだが・・・ ## サイトを最初に開いたときにアクセス制限が機能しない・・・だと <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdBh5_6Fu3WkbvWkevfGsBDJruG7rYaeexPRY4d2SRrmm74fdhkIguVZ_tvfx1cHHwgaFoFzwsIey9q4DwtM4zg68acUGq_EQr8iYX3DHhxDDIKl6-ri8t7xHcMtwjTUeLurA65pXgcgY/s1600/be_depressed.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdBh5_6Fu3WkbvWkevfGsBDJruG7rYaeexPRY4d2SRrmm74fdhkIguVZ_tvfx1cHHwgaFoFzwsIey9q4DwtM4zg68acUGq_EQr8iYX3DHhxDDIKl6-ri8t7xHcMtwjTUeLurA65pXgcgY/s1600/be_depressed.png" data-original-width="58" data-original-height="111" /></a></div> <div style="text-align: center;">これに気がついたときのテンション</div> クッキーに認証用データを保存はしたのだが、処理自体はVuexのステート内にあるデータを使用しているため、そちらにクッキーのデータを反映する必要があった。 クッキーにユーザー認証データを保存するのは簡単だったのだが、それを参照する方法に戸惑ってしまったのが今回の一番大きな問題である。 そのためミドルウェアを利用したのだが、Nuxt.jsの特徴であるサーバーサイドレンダリングあたりでハマってしまった。 Nuxt.jsはサーバーサイドレンダリングに対応しているためミドルウェアはサーバーサイドでも動作しうる。 そのタイミングはよくわかっていないが、サイトに最初にアクセスしたときはサーバーサイドでミドルウェアが実行される。 クッキーはブラウザに保存されるものなので、サーバー側ではブラウザ側と同じ方法ではアクセスできない。(代わりにHTTPヘッダーを参照する) このような感じで、クッキーのデータをVuexへ反映できない => 認証がうまく働いていない という結果になってしまった。 解決策はミドルウェアの処理でサーバー側とブラウザ側の二通りの処理を作り、呼び出されている側を認識して切り替えることだ。 `process.server`でサーバー側なのか判断できる。 ```js export default context => { if (process.server) { // サーバー側 context.req.headers.cookie // ... } else { // ブラウザ側 Cookie.get('') // ... } } ``` これで無事解決できた。 ## 終わりに HTTP通信にはセッションというものもあり、そちらでもデータを記録できるが今回はVuexで肩代わりできているので使っていない。 <span style="font-size: 8px">もしかしたら、Vuexの内部で使っているかもしれないけど・・・</span>
(画像は以下のものを使用させていただきました。)
2019年3月20日水曜日
3つの鍵
サイトをHTTPSにする際もちろんOpenSSLで秘密鍵と公開鍵を用意する必要があるので、開発用にオレオレ証明証をとりあえず作成している。 一応開発時にもHTTPSでサーバーを立てているので、なんか鍵を用意するしている。 結構このあたりの知識はあやふやで、公開鍵と秘密鍵の二つ必要だったけ、とOpenSSLで作成している。 ```bash openssl genrsa -out develop.key 4096 openssl rsa -in develop.key -out develop.crt -pubout ``` (開発用なのでAESで暗号化まではしてないです・・・) のだが、これを使ってnode.jsのHTTPSサーバーを立てるとエラーが起きた。 ```bash (node:5695) UnhandledPromiseRejectionWarning: Error: error:0906D06C:PEM routines:PEM_read_bio:no start line ``` <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtR9ntWQ125x-Zi_27VDmBwSGteuZV75rp0LSCRAIpn89XDmMiAlJXtc5QqtscXxD-nlgX0jUx8j9U1UzuPjDf5mvDUJt90aO9BNSf2UO58sNqYy364i1nSm4cSQahmnAnUDZkueuKsMA/s1600/wait.png" data-original-width="60" data-original-height="105" /></a></div> <div style="text-align: center;">鍵がおかしいっていわれているぞ?</div> ちゃんと公開鍵と秘密鍵の二つ用意しているはずなのでエラーが起きている。 なので検索してみると、公開鍵を設定するのではなくてデジタル証明書を設定する必要があるとのこと <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align: center;">鍵が3種類もあるだと!?</div> 公開鍵だけだとその鍵が本当に使用者のものなのかわからないため、その鍵の所有者を証明するデジタル証明書が必要になる。 デジタル証明書もOpenSSLで作成でき、その際に秘密鍵と公開鍵の二つ指定する必要がある。 ```bash openssl x509 -in ssl/develop.csr -days 565000 -req -signkey ssl/develop.key > ssl/develop.crt ``` このデジタル証明書、一般公開する際は認証局(CA:Certificate Authority)という機関に申請し許可を得たものを使用しなければいけない。 そもそもなんで認証局が必要かというと、通信途中のルータやらなんやらの中で公開鍵の改竄ができてしまうからだ。 なので、セキュリティ的に重要なものには必ず認証局のお墨付きをもらったものを使いましょう。 今回は開発用なのでいわゆるオレオレ証明書を使う。 ## 3つの鍵の効果 秘密鍵と公開鍵、デジタル証明書を使うことで以下の恩恵を受けることができる。 1. 通信内容の暗号化 2. 通信相手の身元証明 あと、改竄があったらわかるという利点もある。 ## 終わりに オレオレ証明書は身元の証明が行えないので、公開するサーバーには決して使わないようにしましょう。 [オレオレ証明書を使いたがる人を例を用いて説得する](https://qiita.com/Sheile/items/dc91128e8918fc823562) <span style="font-size: 6px;">けど、認証局のお墨付きはお金かかるし個人的なちょっとしたものにはこっそりと使う分には多めに見てほしい</span>
(画像は以下のものを使用させていただきました。)
2019年3月19日火曜日
ログを残す
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZnZq5g-MlLNfMbbH-RxMA1m4V3ASTv0wE5WNMTouPI0vA4hbB-ponzirEzCF-GnuGDXwM1W99vRx4lVNgtgY0jidxBv3abKt8uES92eME9eOV-RpBVTA_R7VsupE6itZzm02-Xes_t-E/s1600/express.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZnZq5g-MlLNfMbbH-RxMA1m4V3ASTv0wE5WNMTouPI0vA4hbB-ponzirEzCF-GnuGDXwM1W99vRx4lVNgtgY0jidxBv3abKt8uES92eME9eOV-RpBVTA_R7VsupE6itZzm02-Xes_t-E/s1600/express.png" data-original-width="75" data-original-height="110" /></a></div> <div style="text-align: center;">うーん、このログ見づらいな・・・</div> と思ったのでログ機能をまとめてみたところ、メッセージの形式に迷ったのでメモを残す。 ## 1. なんか英文 ``` !!!! ERROR !!!! failed to XXX... Detect to invalid value 'XXX'... ``` デバッグログの延長線上みたいな感じに ## 2. 日時ないといつ起きたかわからなくないかい? <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align: center;">本当だ、日時がないといつ書き込まれたかわからないぞ</div> 日時は重要なので書き足す。 ``` [20XX-01-01 12:12:12] !!!! ERROR !!!! failed to XXX... param1=XXX [20XX-12-01 12:12:12] Detect to invalid value 'XXX'... ``` うんうん、なんかそれっぽい感じになってきた! ## 3. 文章より決まった形式があったほうがいいのでは? 結構ログメッセージを考えるの面倒なんだよね、というわけであまりメッセージを書かない感じに ``` [20XX-01-01 12:12:12] [ERROR] in Bar.js:10 Func1() param1=XXX [20XX-12-01 12:12:12] [WARNING] in Foo.js:231 Func2() value=XXX ``` なんだかコンパイルエラーメッセージっぽくなった気がする メッセージでエラーか単なるログなのか判断するより、タグ的なものをつけたらわかりやすいし、さらにファイルと行番号、関数名まで残したらデバッグのとき便利なんじゃないかな ## 4. JSON形式でいいんじゃね? ここまで来たら、JSON形式にしちゃおう。 JSON形式ならツール作るときにも簡単に読み込めるし、そのままでも読めるからいいんじゃない? ``` {"timestamp": "20XX-01-01 12:12:12", "type": "ERROR", file: "Bar.js", "line": 10, func:"Func1", message="param1=XXX"} {"timestamp": "20XX-12-01 12:12:12", "type": "WARNING", file: "Foo.js", "line": 231, func:"Func2", message="value=XXX"} ``` 一行は長くなったけどいいんじゃないかな? 以上、終わり。 ## あとがき 実際には[log4js](https://github.com/log4js-node/log4js-node)を使用して、3.の方法でログ出力しています。 JSON形式まではしなくてもいいじゃないかなと、ログ解析用ツールを作るなら別かもしれないけど。 それはそうと出力されたログを読んでいるとサーバー作っているんだなっていう気分が生まれてくるのは気の所為ではないはず。
(画像は以下のものを使用させていただきました。)
2019年3月18日月曜日
System limit for number of file watchers reached, watch 'Filepath'
nodemonでファイルが更新されたとき自動リロードするようにしているところ、また新しいエラーと遭遇した。 ```bash [nodemon] Internal watch failed: ENOSPC: System limit for number of file watchers reached, watch '<watch-target-path>' ``` <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_nsvkWKc_DoqtthCO0pN85h3JPKt4RUAfjqyjXwGSsmHoHLdinZ2jxR4qKW3b-3TYWn8xnZjjT6NP-5FeVSKv89NmWQF9nKRrigrWphuhHvN6wsDASe1x_coCOA6Xung2bQZNi0bXKoM/s1600/be_surprised.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_nsvkWKc_DoqtthCO0pN85h3JPKt4RUAfjqyjXwGSsmHoHLdinZ2jxR4qKW3b-3TYWn8xnZjjT6NP-5FeVSKv89NmWQF9nKRrigrWphuhHvN6wsDASe1x_coCOA6Xung2bQZNi0bXKoM/s1600/be_surprised.png" data-original-width="80" data-original-height="139" /></a></div> <div style="text-align: center;">ああ、これはわかる。</div> さっきパッケージマネージャで依存パッケージをインストールしたら<a href="https://code.visualstudio.com/docs/setup/linux#_visual-studio-code-is-unable-to-watch-for-file-changes-in-this-large-workspace-error-enospc">VSCODEがworkspaceにあるファイル数が多くて監視しきれないって警告</a>をしてくれたからそれ関連だろうな。 とvscodeの設定を確認してみたところ、すでにnode_modulesは除外リストに登録されていた。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> <div style="text-align: center;">(VSCODEを再起動したら無事動きました。)</div> 今回のエラーはファイル監視上限を超えたというOS絡みのエラーで、上のエラーメッセージで検索してみるといろんなコマンドで同じエラーが発生しているのがみれる。 上限を超えてしまったのでどうしようもないのだが、その上限はこちらで設定することができる。 ## 監視上限の設定方法 [参考ページ](https://askubuntu.com/questions/770374/user-limit-of-inotify-watches-reached-on-ubuntu-16-04) 更新の監視を行うことができるファイルの上限は以下のコマンドで確認でき、 ```bash cat /proc/sys/fs/inotify/max_user_watches ``` 次のコマンドで上限を変更できる。 ```bash echo fs.inotify.max_user_watches=<監視上限数> | sudo tee -a /etc/sysctl.conf sudo sysctl -p ``` ファイルの監視はカーネル側の領域で`sysctl`の設定を変更する必要がある。 上のコマンドはその設定ファイルに監視上限を付け加えるものになる。 `sysctl`の設定項目は`sysctl -a`で確認できる。(manには書いていなかった) `fs.inotify.max_user_watches`が監視上限パラメータになっているが、その中の<a href="https://ja.wikipedia.org/wiki/Inotify">`inotify`はLinuxカーネルサブシステム</a>を指しているそうだ。
(画像は以下のものを使用させていただきました。)
2019年3月17日日曜日
GithubとSSH通信を行おうとしたら権限がないと出た
GithubでSSHを使ってリポジトリをクローンしたりプッシュしたりとなにか通信を行ったときに以下のようなエラーが出た。 ```bash > git clone <repository> ... permission denied (publickey). ... ``` <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSIgaNs_Gn2upPJ_2hpeKX8ipWv72bwDS9l0xc3NLABbm_V82BalMiEjLc6T5zC5VYcbYttjeLGoNSN9QVQk601b4seu2UjkVtLW94BlZODZ-eh91ufY2ezTagRAHq6XR3Nc-xvrNgiA/s1600/adapt_to_comics.png" data-original-width="210" data-original-height="135" /></a></div> 登録したときは問題なく接続できたし、Githubの設定を見てもキーは存在していた。 なので<a href="https://tositeru.github.io/Notebook/ssh/top.html#permission-denied-publickey-と出たときの対処法">ちょっと調べてみたら</a>、sshを管理しているプログラムがGithubに登録しているキーを認識していなかったのが原因。 リンク先の解決法を行うと問題なく通信できたので、一見落着かと思いきや、他のTerminal上やTerminalを開き直すとまた同じエラーが出たので、Githubのドキュメントを見ると指定した名前のキーを用意してほしいとのこと。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlIVUj4aZ4YH1LABROAfSSqQe1cBgxHjG13lN_Gi7WE5X5FXcTd2Y6xV8BXRdEb4U-tBn24i3L1e1TntG44aYlMpms1WrH6q9Vn5r0IpSUBiXgirfQ3dOtrFSczGmKex5rv7Cfkf1v4W0/s1600/wait.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlIVUj4aZ4YH1LABROAfSSqQe1cBgxHjG13lN_Gi7WE5X5FXcTd2Y6xV8BXRdEb4U-tBn24i3L1e1TntG44aYlMpms1WrH6q9Vn5r0IpSUBiXgirfQ3dOtrFSczGmKex5rv7Cfkf1v4W0/s1600/wait.png" data-original-width="60" data-original-height="105" /></a></div> そうなのか・・・ 専用の名前をつけたのがアダになったのか・・・ いや、専用の名前をつけてほしいと書かれているのだけど。 と、改めてGithubドキュメントに従ってキーを再登録したら問題解決。無事GithubでSSHを使えるようになりました。
(画像は以下のものを使用させていただきました。)
2019年3月15日金曜日
JsDocからTypeScriptの型定義を移植するとき困ったことのまとめ
TypeScriptから型定義のないJSモジュールを使う機会があったので、せっかくなので勉強がてら型定義ファイルを自作してみた。 移植元のコードにはJSDoc形式のコメントがあったので、あらかた困ることはなかったのだが、いくつかどう型の定義を書けばいいのかわからなかった部分があったのでそれをメモしていく。 ## 辞書型 ```js /** * @property {Object<string, number>} [var] ``` stringをキーでnumberを値とするよくあるjsのオブジェクトだが、そのまま`Object<string, number>`と書くと、`Type 'Object' is not generic.`とエラーが出る。 TypeScriptではObjectはクラスに置き換わる形になるので、単に辞書型を定義する方法がよくよく考えてみたら思いつかない。 なので調べたところ以下のように書く。 ```ts var1: {[id:string]: number}; var2: {[id:string]: {[id: number]: C}} ``` ## 関数 関数も少し悩んだ。 ```js * @property {function(number, Bar):number} [func] ``` これは以下のように書く。 arg1とarg2は異なるものでもいい。 ```ts func: (arg1: number, arg2: Bar) => number; ``` ## 終わり 今回すでにGithub上でTypeScriptの型定義ファイルが公開されているものがあったが、 勉強のため一から自作してみたところやっぱり大変な作業であったので、 公開してくれている方には感謝する他ないとそのありがたみを感じたところでこの記事を閉じる。
公開してくれている人ありがとう!
(画像は以下のものを使用させていただきました。)
2019年3月10日日曜日
Authenticate a Node.js API with JSON Web Tokensを読んだ。
### 初めに ユーザー認証を実装しようと思ったところ、 ログインに成功した後サーバーとクライアントの両側でどのようにログイン状態を確認するのかよくわかっていなかったので、 ネットサーフィンしていたら良さそうな解説サイトがあったので簡単に翻訳してみる。 Nuxt.jsの例を元にできそうだが、認証周りはセキュリティ的に少し慎重に作りたいので解説サイトがあって助かった。 [Authenticate a Node.js API with JSON Web Tokens](https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens) 以下、その簡約 ## 概要 予備知識として[The Ins and Outs of Token Based Authentication](https://scotch.io/bar-talk/the-ins-and-outs-of-token-based-authentication)と [JSON Web Tokens](https://scotch.io/tutorials/the-anatomy-of-a-json-web-token)を読んでほしい。 ## 開発環境 NodeとExpress、POSTmanを利用する。 作業手順は以下の通り、 1. 保護されたまたはされないルートを持つ。 1. ユーザーは名前とパスワードを使った認証に成功したらトークンを受け取る 1. ユーザーはクライアント側でそのトークンを格納し、サーバーへリクエストを送るときはそれも一緒に送る。 1. サーバー側は送られてきたトークンを検証し、有効なものであるならJSONデータを返す。 ルートについては以下の種類が出来上がる。 1. 通常のルート 1. トークンを検証するルートミドルウェア 1. 名前とパスワードを認証し、トークンを返すルート 1. 全ユーザーを取得する認証されたルート ## 下準備 プロジェクトルートの`server.js`をサーバーのエントリポイントとして開発していく。 プロジェクトの作成ができたら、以下のパッケージを追加する。 - express - body-parser - morgan - mongoose - jsonwebtoken ### Userモデル `app/models/user.js`にデータベース上のユーザーのテーブルを定義する。 ```js const mongoose = require('mongoose') const Schema = mongoose.Schema module.exports = mongoose.model('User', new Schema({ name: String, password: String, admin: Boolean })) ``` MongoDBを使う時は設定ファイルが必要になるのでそれも作成する。 ちなみにMongoDBのアカウントを作成し、データベースを構築する必要がある。 一応料金フリーのものがあるので試しやすい。 ```js //config.js module.exports = { 'secret': 'ilovescotchyscotch', 'database': 'mongodb://noder:noderauth&54;proximus.modulusmongo.net:27017/so9pojyN' } ``` ## サーバーの実装 ユーザーデータを使う準備ができたので、`server.js`に今回説明するものを全て書いていく。 大体以下の内容を持つようになる。 - アプリケーションの設定 - 基本的なルートの作成: `http://localhost:8080`がホームページになる。 - APIルートの作成 - `POST http://localhost:8080/api/authenticate` : 認証処理を行う - `GET http://localhost:8080/api` : トークンを持っていたらランダムに生成されたテキストを表示する - `GET http://localhost:8080/api/users` : トークンを持っていたら全ユーザー表示する 次のコードを元に機能を追加していく ```js // ======================= // get the packages we need ============ // ======================= var express = require('express'); var app = express(); var bodyParser = require('body-parser'); var morgan = require('morgan'); var mongoose = require('mongoose'); var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens var config = require('./config'); // get our config file var User = require('./app/models/user'); // get our mongoose model // ======================= // configuration ========= // ======================= var port = process.env.PORT || 8080; // used to create, sign, and verify tokens mongoose.connect(config.database); // connect to database app.set('superSecret', config.secret); // secret variable // use body parser so we can get info from POST and/or URL parameters app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); // use morgan to log requests to the console app.use(morgan('dev')); // ======================= // routes ================ // ======================= // basic route app.get('/', function(req, res) { res.send('Hello! The API is at http://localhost:' + port + '/api'); }); // API ROUTES ------------------- // we'll get to these in a second // ======================= // start the server ====== // ======================= app.listen(port); console.log('Magic happens at http://localhost:' + port); ``` ## テスト用のユーザー作成 `localhost:8080/setup`でテスト用のユーザーを作成する。 サンプルだからそのまま保存しているが、パスワードは決してそのままデータベースに保存しないこと ```js app.get('/setup', function(req, res) { // create a sample user var nick = new User({ name: 'Nick Cerminara', password: 'password', admin: true }); // save the sample user nick.save(function(err) { if (err) throw err; console.log('User saved successfully'); res.json({ success: true }); }); }); ``` ## サンプル用のユーザーを作ったので先にトークンなしで`http://localhost:8080/api`と`http://localhost:8080/api/users`を作る。 ```js // API ROUTES ------------------- // get an instance of the router for api routes var apiRoutes = express.Router(); // TODO: route to authenticate a user (POST http://localhost:8080/api/authenticate) // TODO: route middleware to verify a token // route to show a random message (GET http://localhost:8080/api/) apiRoutes.get('/', function(req, res) { res.json({ message: 'Welcome to the coolest API on earth!' }); }); // route to return all users (GET http://localhost:8080/api/users) apiRoutes.get('/users', function(req, res) { User.find({}, function(err, users) { res.json(users); }); }); // apply the routes to our application with the prefix /api app.use('/api', apiRoutes); ``` それでは本題の認証状況に応じてリクエストを許可したりしなかったりをできるようにしていく。 ## 認証処理 ### 認証とトークン作成 認証は上で書いた通り`POST http://localhost:8080/api/authenticate`で行う。 フォーム用のページは作っていないが、その代わりに[POSTman](https://www.getpostman.com/)というツールを使用する。 なお、バージョンアップなどでところどころ動かないところがあったので元記事より修正している。 (元記事は2015年に書かれている。コメントを見る限り) ```js // API ROUTES ------------------- // get an instance of the router for api routes var apiRoutes = express.Router(); // route to authenticate a user (POST http://localhost:8080/api/authenticate) apiRoutes.post('/authenticate', function(req, res) { const user = await User.findOne({name: req.body.name}) if (!user) { return res.json({ success: false, message: 'Authentication failed. User not found.' }); } // check if password matches if (user.password != req.body.password) { res.json({ success: false, message: 'Authentication failed. Wrong password.' }); } else { // if user is found and password is right // create a token with only our given payload // we don't want to pass in the entire user since that has the password const payload = { admin: user.admin }; var token = jwt.sign(payload, app.get('superSecret'), { expiresIn: 60 * 60 * 24 // expires in 24 hours }); // return the information including token as JSON res.json({ success: true, message: 'Enjoy your token!', token: token }); } }); ... ``` 認証に成功したら(コードの最後あたり)jsonwebtokenを使って認証用のトークンを作成している。 このトークンを使い権限の確認を行っていく。 生成に使用するアルゴリズムは`options.algorithm`で指定する。 何も指定しなければ`HS256`になる。 ```js var token = jwt.sign(payload, app.get('superSecret'), { algorithm: 'HS256', expiresIn: 60 * 60 * 24 // expires in 24 hours }); ``` ## APIルートを保護するためのルートミドルウェア これまでで`/api/authenticate`と`/api`、`/api/users`の3つのAPIを実装してきた。 次はこれらの内`/api`と`/api/users`を認証状況に合わせてアクセスの許可/不許可を行えるようにする。 この記事で一番重要な箇所だ。 リクエスト内のトークンの確認を行うルートミドルウェアは以下のものになる。 トークンがなければHTTPレスポンスコード403(閲覧禁止)を返す。 ```js // route middleware to verify a token apiRoutes.use(function(req, res, next) { // check header or url parameters or post parameters for token var token = req.body.token || req.query.token || req.headers['x-access-token']; // decode token if (token) { // verifies secret and checks exp jwt.verify(token, app.get('superSecret'), function(err, decoded) { if (err) { return res.json({ success: false, message: 'Failed to authenticate token.' }); } else { // if everything is good, save to request for use in other routes req.decoded = decoded; next(); } }); } else { // if there is no token // return an error return res.status(403).send({ success: false, message: 'No token provided.' }); } }); ``` クライアント側では取得したトークンをHTTPヘッダーの`x-access-token`や リクエスト本体、URLパラメータの好きなところに設定すればいい。 サーバー側で受け取ったトークンは作成した時に使用したキーを使って復元している。 また期限切れになっているかは`options.maxAge`を指定してあげたら確認できる。 期限切れになっていたらエラーを返すので、自前でチェックコードを追加する必要はない。 ## 終わり 認証はどう正しく実装したらいいのかわからなかったが、この記事は一つ一つ丁寧に解説してくれていたので理解できたと思う。 さらにトークン作成に使っている[jsonwebtokenの](https://github.com/auth0/node-jsonwebtoken)ドキュメントを見ると、暗号化には秘密鍵とかも利用できるようなので攻撃者による復号化も困難と非常に安心できる手法だと思う。 またMongoDBやPOSTmanといったものにも触れる機会ができたのも非常に良かった。 ### 追記 node.jsで認証を行うなら[Passport](http://www.passportjs.org/)を使用するのがいいみたい。
2019年3月8日金曜日
Sequelizeでupsertしたときのエラーがおきたのでコードを少し読んだときのこと
ORMにSequelizeを使っているのだが、モデルのupsertを使った時に`SQLITE_ERROR: no such column: id`とエラーが発生した。 原因はテーブル作成時にプライマリーキーに指定していたカラムをIDとは異なるものにしていたのだが、それに合わせてモデル定義時に明示的にプライマリーキーを指定する必要があるのだが、それをすっかり忘れていただけであった。 ```js const UserTmp = sequelize.define('UserTmp', { need_primary: { type: DataTypes.STRING, primaryKey: true, // <- 忘れていた }, }, {}); ``` これだけだとあれなので、デバッグのとき`upsert`のオプションにプライマリキーを指定する必要があるのかと当初は思い、 ちょっとSequelizeのコードを読んで見たところ`upsert`する前にHook関数を呼び出すことができることを発見した。 [Sequelize Hooks](http://docs.sequelizejs.com/manual/tutorial/hooks.html#hooks) ```js //モデル定義のとき const UserTmp = sequelize.define('UserTmp', { need_primary: { type: DataTypes.STRING, primaryKey: true, // <- 忘れていた }, }, { hooks: { beforeUpsert(values/*upsertに渡した値*/, options/* upsertに渡したオプション */) { console.log() } } }); ``` また、`Promise.try`というものも初めて見かけた。 Bluebirdというパッケージのものらしい。標準のPromiseより処理が速いそうだ。
2019年3月7日木曜日
Firefoxでログ出力したら値が正しく表示されなかったのことについて
Firefoxにて、配列を含んでいるオブジェクトを表すJSONを`JSON.parse`を使ってオブジェクトに戻した時に起きた。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwdJE_sPqMbMtAmLqjf1yf8pEtTkEKA-_pnc0ZpfO2JUmbBKK7i-8F0DLFgNkIw9N_KPsEZcIHXEOclDvHS1I-8K0G6KdBTdEt8PaeNOlDdK6iHN1XzFxjHqmxDtVgCF79QjPAXRD5gdo/s1600/Screenshot+from+2019-03-07+13-37-16.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwdJE_sPqMbMtAmLqjf1yf8pEtTkEKA-_pnc0ZpfO2JUmbBKK7i-8F0DLFgNkIw9N_KPsEZcIHXEOclDvHS1I-8K0G6KdBTdEt8PaeNOlDdK6iHN1XzFxjHqmxDtVgCF79QjPAXRD5gdo/s320/Screenshot+from+2019-03-07+13-37-16.png" width="271" height="320" data-original-width="704" data-original-height="832" /></a></div> おかしい方には`__defineGetter__: function __reactiveGetter__`と`__defineSetter__: function __reactiveSetter__`がある。 検索してもはっきりとした情報はなく`Vue.js`の影響っぽい感じがある。 コード的にも文字列に対して`JSON.parse`を適応しているだけで、[axios](https://github.com/axios/axios)の`transformResponse`の中で使うと何やらこれが起こるみたいだ。 ## 解決策 オブジェクトを出力する時は以下のようにしたらいい。 [MDN](https://developer.mozilla.org/ja/docs/Web/API/Console/log#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E3%83%AD%E3%82%B0)にもそう書いてあった。 ```js let obj = { hoge: [100, 200, 300] } console.log(JSON.parse(JSON.stringify(obj))) ```
新しい投稿
前の投稿
ホーム
登録:
投稿 (Atom)