- 投稿日:2020-11-19T23:16:04+09:00
command not found: nodeエラーは、"nodebrew use" で解決する
直面した問題
Terminal% nodebrew list v15.2.1 current: none % % node -v zsh: command not found: node %以下のように、パスを通したり、いろいろやってもnodeコマンドは認識されない。
・@kyosuke5_20さんのQiita記事「kyosuke5_20」
・「node がインストールできない」nodebrew use vバージョン番号を実行すると、nodeコマンドが認識された。
Terminal% nodebrew use v15.2.1 use v15.2.1 % nodebrew list v15.2.1 current: v15.2.1 % % node -v v15.2.1 %
- 投稿日:2020-11-19T16:24:19+09:00
Webアプリ未経験者がReactに手を出す
妻が「こんなアプリがあったらいいのに」と話していたのを聞いて、ノリでちょっと作ってみようと思ったのだが、そもそもC言語しか知らない人間にとっては「WebアプリってCSSとJavascriptで作るもんでしょ?」ぐらいしか知識がなく。
しかし、断念するのもアレなので色々とググったら、ReactとかVue.jsとかどっかで見たことある言葉がたくさん出てきてしまって更に悩むことになったが、なんとなくReactを使おうと決めた。
Reactのことなら巷に情報は溢れているし、Webアプリ開発者の方々ならサクサク進めるであろうが、未経験者はいちいち躓いていくという現実を、備忘録込みで記録を残してみようと思う。
今の理解
Reactについての認識はこんなところ。
- WebのUI用JavaScriptライブラリ群
- これを使えばWeb画面であれやこれやできる
- Facebookが開発しているOSSで、今は商用に使っても大丈夫
また、このReactを用いてWebアプリ開発を行うには、以下の2つのパッケージが必要と知った。
- npm ・・・ 「Node.js」に関連するパッケージを独自で管理するアプリケーションのこと
- Node.js ・・・クライアントだけでなくサーバ側でも動作するJavascript実行環境で、Reactでの生産物をサーバ側で動作させるための土台
ということでnpmとnode.jsを手元に揃えるところから着手する。
開発環境の準備
開発環境は以下のスペック(古いノート)。
- PC:FMV-BIBLO MG70W(2007年製)
- CPU:Core2Duo T7100@1.80GHz
- Mem:4GB
- SSD:50GBぐらい
- OS:Linux Mint 20 Cinnamon
インストール
aptを使って「npm」をインストールする。
$ sudo apt install npm : $ npm -v 6.14.4 $ sudo apt install nodejs パッケージリストを読み込んでいます... 完了 依存関係ツリーを作成しています 状態情報を読み取っています... 完了 nodejs はすでに最新バージョン (10.19.0~dfsg-3ubuntu1) です。 nodejs は手動でインストールしたと設定されました。 アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。 $ nodejs -v v10.19.0「nodejs」はnpmと依存関係ということで自動的に入っていた。
バージョンアップ
執筆時点(2020/11/19)でのバージョンは以下となっていた。
npm Node.js Mint 20 6.14.4 10.19.0 安定版 6.14.8 14.15.1 最新版 7.0.12 15.2.1 npmはまだしも、Node.jsはバージョンが離れすぎ。
Node.js
先にNode.jsをアップデートする。
(1) npmでNode.jsのパッケージ管理アプリの「n」をインストール
$ sudo npm cache clean $ sudo npm install -g n(2) nを使って、安定版(stable)を導入する
$ sudo n stable : installed : v14.15.1 (with npm 6.14.8) $(3) バージョンを確認する
$ nodejs -v v10.19.0 $ node -v v14.15.1ん?どういうことだ??
$ which node /usr/local/bin/node $ ls -l /usr/local/bin/node* -rwxr-xr-x 1 root root 73828464 11月 19 13:19 /usr/local/bin/node $ which nodejs /usr/bin/nodejs $ ls -l /usr/bin/node* -rwxr-xr-x 1 root root 14416 3月 31 2020 /usr/bin/node lrwxrwxrwx 1 root root 40 3月 7 2020 /usr/bin/node-gyp -> ../share/nodejs/node-gyp/bin/node-gyp.js lrwxrwxrwx 1 root root 4 3月 31 2020 /usr/bin/nodejs -> node $ env | grep PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin/usr/local/binにはnodejsがないので、/usr/binにある方が動いてるのはわかるが、ファイルサイズが全然違うし、どないなってんのやん??
(4) aptでインストールしたnodejsとnpmを削除する
調べたところ、aptで入れた「nodejs」と「npm」は上記の新しいバージョンを導入するために必要としただけであり、もう用済みであることがわかった。ということでaptで削除する。$ sudo apt purge -y nodejs npm : (ガッツリ削除されるが気にしない) : $(5) 再度、バージョンを確認する
$ npm -v 6.14.8 $ node -v v14.15.1 $ nodejs -v bash: /usr/bin/nodejs: そのようなファイルやディレクトリはありません $nodejsってのは古いコマンド表記なのかな?
とりあえず入れ替わったし、このままとする。npm
一方のnpmだが、バージョンアップしたNode.js 14.15.1にnpm6.14.8が同梱されていたため、知らずにバージョンアップが済んでしまっていた。
$ npm -v 6.14.8 # 個別にバージョンアップする場合は「sudo npm install -g npm」を実行するHello World!
これで開発環境は出来上がったので、定番の「Hello World」をやってみた。
しかし、プログラムというと「viで.cファイル開いてmake」の認識であり、Eclipseですらほとんど触ったこと無いので、今のモダンな開発手法・ツールにいちいち驚く始末。
(1) 「Create-React-App」で雛形を作る
昔ながらにviで1ファイルずつ丹精込めて・・・でもいつかは出来るだろうが、Reactにはアプリ開発するのに必要な雛形が提供されているとのことなので、未経験者としてはこれを使わない手は無い。$ npx create-react-app helloworld : (ズラズラズラズラと結構時間がかかった) : Happy hacking! $ ls -l 合計 4 drwxrwxr-x 5 xxx xxx 4096 11月 19 14:50 helloworld $(2) 動作確認
この雛形のみでもブラウザで確認できるというので、以下の手順を実行。ブラウザから「localhost:3000」にアクセスするとReactの画面が表示された。$ cd helloworld $ npm start : (しばらく時間がかかった) : Compiled successfully! You can now view helloworld in the browser. Local: http://localhost:3000 On Your Network: http://xxx.xxx.xxx.xxx:3000 Note that the development build is not optimized. To create a production build, use npm run build.(3)「Hello World」の実現
ブラウザに「Edit src/App.js and save to reload」とあるので、これに従ってApp.jsを編集(Hello Worldを追記)→保存→ブラウザをリロードした(npmを再実行しなくてよい)。$ cd src $ vi App.js import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> + <p> + Hello World!! + </p> <img src={logo} className="App-logo" alt="logo" /> : (省略) : </header> </div> ); } export default App; $雑感
- 本当にひとまずWebアプリを作れる環境は出来上がったが、大抵「Hello World」で終わることが多いので、ここから続けられるのかが一番の課題。
- 作りたいもののイメージはあって、ロジックだけPythonで試作してみていけそうな感じはしているのだが、Webアプリのお作法が全くわかっていないので、Reactを利用しながら進められるのか不安。
- 次はどこに進めばいいのだろうか?
- 投稿日:2020-11-19T16:24:19+09:00
Webアプリ未経験者のReact入門
妻が「こんなアプリがあったらいいのに」と話していたのを聞いて、ノリでちょっと作ってみようと思ったのだが、そもそもC言語しか知らない人間にとっては「WebアプリってCSSとJavascriptで作るもんでしょ?」ぐらいしか知識がなく。
しかし、断念するのもアレなので色々とググったら、ReactとかVue.jsとかどっかで見たことある言葉がたくさん出てきてしまって更に悩むことになったが、なんとなくReactを使おうと決めた。
Reactのことなら巷に情報は溢れているし、Webアプリ開発者の方々ならサクサク進めるであろうが、未経験者はいちいち躓いていくという現実を、備忘録込みで記録を残してみようと思う。
今の理解
Reactについての認識はこんなところ。
- WebのUI用JavaScriptライブラリ群
- これを使えばWeb画面であれやこれやできる
- Facebookが開発しているOSSで、今は商用に使っても大丈夫
また、このReactを用いてWebアプリ開発を行うには、以下の2つのパッケージが必要と知った。
- npm ・・・ 「Node.js」に関連するパッケージを独自で管理するアプリケーションのこと
- Node.js ・・・クライアントだけでなくサーバ側でも動作するJavascript実行環境で、Reactでの生産物をサーバ側で動作させるための土台
ということでnpmとnode.jsを手元に揃えるところから着手する。
開発環境の準備
開発環境は以下のスペック(古いノート)。
- PC:FMV-BIBLO MG70W(2007年製)
- CPU:Core2Duo T7100@1.80GHz
- Mem:4GB
- SSD:50GBぐらい
- OS:Linux Mint 20 Cinnamon
インストール
aptを使って「npm」をインストールする。
$ sudo apt install npm : $ npm -v 6.14.4 $ sudo apt install nodejs パッケージリストを読み込んでいます... 完了 依存関係ツリーを作成しています 状態情報を読み取っています... 完了 nodejs はすでに最新バージョン (10.19.0~dfsg-3ubuntu1) です。 nodejs は手動でインストールしたと設定されました。 アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。 $ nodejs -v v10.19.0「nodejs」はnpmと依存関係ということで自動的に入っていた。
バージョンアップ
執筆時点(2020/11/19)でのバージョンは以下となっていた。
npm Node.js Mint 20 6.14.4 10.19.0 安定版 6.14.8 14.15.1 最新版 7.0.12 15.2.1 npmはまだしも、Node.jsはバージョンが離れすぎ。
Node.js
先にNode.jsをアップデートする。
(1) npmでNode.jsのパッケージ管理アプリの「n」をインストール
$ sudo npm cache clean $ sudo npm install -g n(2) nを使って、安定版(stable)を導入する
$ sudo n stable : installed : v14.15.1 (with npm 6.14.8) $(3) バージョンを確認する
$ nodejs -v v10.19.0 $ node -v v14.15.1ん?どういうことだ??
$ which node /usr/local/bin/node $ ls -l /usr/local/bin/node* -rwxr-xr-x 1 root root 73828464 11月 19 13:19 /usr/local/bin/node $ which nodejs /usr/bin/nodejs $ ls -l /usr/bin/node* -rwxr-xr-x 1 root root 14416 3月 31 2020 /usr/bin/node lrwxrwxrwx 1 root root 40 3月 7 2020 /usr/bin/node-gyp -> ../share/nodejs/node-gyp/bin/node-gyp.js lrwxrwxrwx 1 root root 4 3月 31 2020 /usr/bin/nodejs -> node $ env | grep PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin/usr/local/binにはnodejsがないので、/usr/binにある方が動いてるのはわかるが、ファイルサイズが全然違うし、どないなってんのやん??
(4) aptでインストールしたnodejsとnpmを削除する
調べたところ、aptで入れた「nodejs」と「npm」は上記の新しいバージョンを導入するために必要としただけであり、もう用済みであることがわかった。ということでaptで削除する。$ sudo apt purge -y nodejs npm : (ガッツリ削除されるが気にしない) : $(5) 再度、バージョンを確認する
$ npm -v 6.14.8 $ node -v v14.15.1 $ nodejs -v bash: /usr/bin/nodejs: そのようなファイルやディレクトリはありません $nodejsってのは古いコマンド表記なのかな?
とりあえず入れ替わったし、このままとする。npm
一方のnpmだが、バージョンアップしたNode.js 14.15.1にnpm6.14.8が同梱されていたため、知らずにバージョンアップが済んでしまっていた。
$ npm -v 6.14.8 # 個別にバージョンアップする場合は「sudo npm install -g npm」を実行するHello World!
これで開発環境は出来上がったので、定番の「Hello World」をやってみた。
しかし、プログラムというと「viで.cファイル開いてmake」の認識であり、Eclipseですらほとんど触ったこと無いので、今のモダンな開発手法・ツールにいちいち驚く始末。
(1) 「Create-React-App」で雛形を作る
昔ながらにviで1ファイルずつ丹精込めて・・・でもいつかは出来るだろうが、Reactにはアプリ開発するのに必要な雛形が提供されているとのことなので、未経験者としてはこれを使わない手は無い。$ npx create-react-app helloworld : (ズラズラズラズラと結構時間がかかった) : Happy hacking! $ ls -l 合計 4 drwxrwxr-x 5 xxx xxx 4096 11月 19 14:50 helloworld $(2) 動作確認
この雛形のみでもブラウザで確認できるというので、以下の手順を実行。ブラウザから「localhost:3000」にアクセスするとReactの画面が表示された。$ cd helloworld $ npm start : (しばらく時間がかかった) : Compiled successfully! You can now view helloworld in the browser. Local: http://localhost:3000 On Your Network: http://xxx.xxx.xxx.xxx:3000 Note that the development build is not optimized. To create a production build, use npm run build.(3)「Hello World」の実現
ブラウザに「Edit src/App.js and save to reload」とあるので、これに従ってApp.jsを編集(Hello Worldを追記)→保存→ブラウザをリロードした(npmを再実行しなくてよい)。$ cd src $ vi App.js import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> + <p> + Hello World!! + </p> <img src={logo} className="App-logo" alt="logo" /> : (省略) : </header> </div> ); } export default App; $雑感
- 本当にひとまずWebアプリを作れる環境は出来上がったが、大抵「Hello World」で終わることが多いので、ここから続けられるのかが一番の課題。
- 作りたいもののイメージはあって、ロジックだけPythonで試作してみていけそうな感じはしているのだが、Webアプリのお作法が全くわかっていないので、Reactを利用しながら進められるのか不安。
- 次はどこに進めばいいのだろうか?
- 投稿日:2020-11-19T11:55:04+09:00
【npx webpack】No valid exports main found for でエラー
【原因】Node.jsのバージョンが古い
Node.jsのバージョンが古い事が原因
僕の場合は
v13.5.0
でした。そこでまずは、こちらの記事のオプション2を参考にnpmを使用して更新しました。
①まずnpmキャッシュをクリアします
npm cache clean -f②続いて、バージョンマネージャーのnをインストール
npm install -g n③最新の安定バージョンをインストールします。
n stable④ここで、Nodeのバージョンを確認してみます。
node -v v14.15.1これで、バージョンが変わっていればOKなのですが、僕の場合はバージョンに変化なしでした。
【原因】リンク先
続いては、こちらの記事を参考に
リンク先を変更してみました。
ln -snf /usr/local/bin/node /usr/bin/node .....Operation not permittedとエラー。
今度はこちらを解決していきます。
【原因】SIP(System Integrity Protection)によって、操作の制限がされている
細かい手順は、こちらの記事がわかりやすかったので参考にしていただければと思います。
node -v v14.15.1これで解決です。
参考
- 投稿日:2020-11-19T08:21:51+09:00
Typescript(Node.js)でtgzを解凍する
Typescriptでtgz(tar.gz)ファイルを解凍する
実装例が少ないことと、紹介されている実装方法では展開先などが指定できなかったりするものがあるため、紹介しておきたいと思います。
使用するパッケージ
よく見る実装方法
import * as fs from 'fs'; import * as zlib from 'zlib'; import * as tar from 'tar'; const tgzPath = '/file/hoge.tgz'; const outPath = '/path/output'; const gunzip = zlib.createGunzip(); const extractor = tar.Extract({path: outputPath}); fs.createReadStream(tgzPath).pipe(gunzip).pipe(extractor);tarパッケージ内でgzipは解凍してくれる
extractメソッドには以下のような説明書きがされている。
/** * Extract a tarball archive. The fileList is an array of paths to extract * from the tarball. If no paths are provided, then all the entries are * extracted. If the archive is gzipped, then tar will detect this and unzip * it. Note that all directories that are created will be forced to be * writable, readable, and listable by their owner, to avoid cases where a * directory prevents extraction of child entries by virtue of its mode. Most * extraction errors will cause a warn event to be emitted. If the cwd is * missing, or not a directory, then the extraction will fail completely. */よりかんたんな実装
実のところ、以下のコードだけでtar.gzを解凍できる。
import * as tar from 'tar'; tar.x({path:'/file/hoge.tgz', cwd:'/path/output'}以上
- 投稿日:2020-11-19T00:01:41+09:00
PWAを試してみよう
扱ったことがなかったので、PWA(Progressive Web Apps) を試してみようと思います。
WebページをPWAとして設定することで以下のことができます。
- Webアプリなのに、ネイティブアプリのように、Android/Windowsにアプリとして登録することができる。
- アドレスバーのようなブラウザっぽさはなく、全画面でネイティブアプリのように起動することができる。
PWAのService Workerの機能を使った実装をすることで、以下のことができます。
- Webコンテンツをキャッシュ化することができ、オフラインで動かすことができる。
- サーバ側からPush通知ができる。
ということで、今回の投稿では、PWAの設定方法と、Push通知の実装をしてみます。
ただ作るだけではモチベーションが上がらないので、パスワード管理アプリ「パスワードマネージャ」を作成します。
世の中にいろいろなツールがありますが、やっぱり自分で管理したいので。。。毎度ながら、ソースコード一式をGitHubに上げておきました。
poruruba/pwa_test
https://github.com/poruruba/pwa_testPWAの設定
以下を準備する必要があります。
- manifest.jsonを作成し、配備します。
- ServiceWorker起動のためのJavascriptを作成し、配備します。
※ただし、上記を配備するWebサーバは、HTTPSである必要があります。
manifest.json
こんな感じにします。
public/manifest.json{ "short_name": "パスワードマネージャ", "name": "パスワードマネージャ", "display": "standalone", "start_url": "index.html", "icons": [ { "src": "img/192x192.png", "sizes": "192x192", "type": "image/png" } ] }上述の通り、最低限192x192のpngファイルが必要です。
あとは、このmanifest.jsonをルートに配置し、index.htmlから、以下のようにして読み出すようにします。public/index.html・・・ <link rel="manifest" href="manifest.json"> ・・・Service WorkerのためのJavascript
こんな感じです。
public/sw.jsvar CACHE_NAME = 'pwa-sample-caches'; var urlsToCache = [ // キャッシュ化したいコンテンツ ]; self.addEventListener('install', function(event) { console.log('sw event: install called'); event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { return cache.addAll(urlsToCache); }) ); }); self.addEventListener('fetch', function(event) { console.log('sw event: fetch called'); event.respondWith( caches.match(event.request) .then(function(response) { return response ? response : fetch(event.request); }) ); }); self.addEventListener('push', function(event){ console.log('sw event: push called'); var notificationDataObj = event.data.json(); var content = { body: notificationDataObj.body, }; event.waitUntil( self.registration.showNotification(notificationDataObj.title, content) ); });補足します。
self.addEventListener('install', function(event) { self.addEventListener('fetch', function(event) {は、Webコンテンツをキャッシュ化する場合に必要です。
ただし、Webコンテンツをキャッシュ化しなくとも、中身は無くてもよいですが、self.addEventListener('install', function(event){ は必要です。self.addEventListener('push', function(event) {は、Push通知を利用する場合に必要です。後述します。
あとは、Webページがロードされたときに、以下を実行すればServiceWorkerが登録されます。Javascriptのファイル名は、sw.jsの前提です。
public/js/start.js・・・ if ('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js').then(async (registration) => { console.log('ServiceWorker registration successful with scope: ', registration.scope); }).catch((err) => { console.log('ServiceWorker registration failed: ', err); }); } ・・・ロードされたかどうかは、Chromeの開発ツールで確認することができます。
F12で開発ツールを開いたのち、Applicationタブを選択すると、左側に「Service Workers」があります。ServiceWorkerのためのJavascriptはソースファイルを更新しても、Chrome内に登録されたものは更新されませんので、この開発ツールから、UpdeteやUnregisterをすることができます。
で、アドレスバーの右隅に、⊕ があるのがわかりますでしょうか。
これが、このWebページがPWAとして登録できることを示しています。
OSやブラウザによってここらへんの表現は異なります。これをクリックすると、こんなのが表示されます。「インストール」ボタンを押下すると、PWAアプリとして登録され、完了すると、以下のように単独のページとして表示されます。
これが、PWAのアプリです。アドレスバーがないのがわかります。Webアプリではなくネイティブアプリに見えますよね。
スタートメニューにも、Chromeアプリのところですが、「パスワードマネージャ」が登録されています。いつでも、これをクリックすることで、アプリとして起動できるようになりました。
Push通知の実装
Node.jsのnpmモジュールのおかげで、サーバ側をすぐに立ち上げることができます。
web-push-libs/web-push
https://github.com/web-push-libs/web-pushまた、パスワードマネージャの機能のために、以下のnpm モジュールも使っています。
uuidjs/uuid
https://github.com/uuidjs/uuidまず最初にするのが、VapidKeyの生成です。
api/controllers/password/index.jsasync function readPasswordFile(apikey){ try{ var pwd = await fs.readFile(FILE_BASE + apikey + '.json', 'utf8'); if( !pwd ){ pwd = { vapidkey : webpush.generateVAPIDKeys(), list: [], objects: {} }; await writePasswordFile(apikey, pwd); }else{ pwd = JSON.parse(pwd); } return pwd; }catch(error){ throw "not found"; } }大事なのは以下の部分です。VAPIKEYという公開鍵ペアの作成です。
vapidkey : webpush.generateVAPIDKeys()
最初に生成した後は、同じ値を使うので、上記の関数の中でこの値をファイルに保存しています。ファイルの中身はこんな感じです。
data/password/test.json{ "vapidkey": { "publicKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "privateKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, list: [], objects: {} }このVapidKeyのうちの公開鍵vapidkey.publicKeyをクライアントに渡します。以下の部分です。通知先クライアントを区別するために、client_idとしてuuidを生成しそれも一緒に渡しています。
api/controllers/password/index.jsif( event.path == '/pwd-get-pubkey' ){ var uuid = uuidv4(); return new Response({ result: { vapidkey: pwd.vapidkey.publicKey, client_id: uuid } }); }elseクライアント側では以下の処理をしています。
public/js/start.jsvar json = await do_post_apikey(base_url + '/pwd-get-pubkey', {}, this.apikey); var object = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: json.result.vapidkey }); await do_post_apikey(base_url + '/pwd-put-object', { client_id: json.result.client_id, object: object }, this.apikey); this.client_id = json.result.client_id; Cookies.set('client_id', this.client_id, { expires: EXPIRES });サーバ側から取得した公開鍵をregistration.pushManager.subscribeに渡しています。
そうすることで、通知のSubscribeが完了し、Push Subscriptionオブジェクトを取得できますので、それをサーバに返してあげます。Push Subscriptionオブジェクトの内容はこんな感じです。
{ endpoint: "https://fcm.googleapis.com/fcm/send/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", expirationTime: null keys: { auth: "XXXXXXXXXXXXXXXXX" p256dh: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } }一緒に、さっきもらったclient_idも一緒にサーバに戻しておきます。client_idのやり取りはPush通知の実装では必須ではないので、適当な実装でもよいです。
サーバ側ではそのPush Subscriptionオブジェクトを受けとって、後でNotificationするときに必要なので、これもファイルに保存しておきます。
api/controllers/password/index.jsif( event.path == '/pwd-put-object' ){ pwd.objects[body.client_id] = body.object; await writePasswordFile(apikey, pwd); await sendNotification(pwd.vapidkey, pwd.objects[body.client_id], { title: "パスワードマネージャ", body: "通知を登録しました。"} ); return new Response({}); }else準備ができたので、後は任意のタイミングで、sendNotification関数を呼び出します。上記のタイミングでも呼び出しています。
中身は以下の感じです。api/controllers/password/index.jsasync function sendNotification(vapidkey, object, content){ var options = { vapidDetails: { subject: NOTIFICATION_SUBJECT, publicKey: vapidkey.publicKey, privateKey: vapidkey.privateKey } }; var result = await webpush.sendNotification(object, Buffer.from(JSON.stringify(content)), options); if( result.statusCode != 201 ) throw "status is not 201"; }webpush.sendNotification() に必要なパラメータを設定して呼び出しています。
通知したい内容は、contentのところで指定します。
例えばこんな感じです。
{ title: "パスワードマネージャ", body: "通知を登録しました。"}
このフォーマットは、なんでもよいのですが、受信する側では把握している前提です。
クライアント側の方は、Push通知を受け付けられるように、コールバック関数を実装しておく必要があります。ServiceWorkerのためのJavascriptに実装します。
public/sw.jsself.addEventListener('push', function(event){ console.log('sw event: push called'); var notificationDataObj = event.data.json(); var content = { body: notificationDataObj.body, }; event.waitUntil( self.registration.showNotification(notificationDataObj.title, content) ); });受信したデータのうち、bodyとtitleを使っています。
Push通知を登録したり解除したりできるようにボタンを用意しておきましたので、「Subscribe」ボタンを押してみましょう。
通知が来ました。
1点注意です。
Windowsで、通知をOffにしていると、いつまでたっても通知は受け取れません。
あらかじめ、通知をOnにしておきましょう。システムの、通知とアクションにあります。
「アプリやその他の送信者からの通知を取得する」 をオンにします。Androidの場合
Androidの場合についても示しておきます。
Webアプリなので、OSに依存せず同じように登録できるのはWebアプリのメリットです。Androidの場合は、PWAアプリとして登録するのは、Chromeブラウザのメニューから「アプリをインストール」を選択することでインストールされます。
タッチすると以下が表示されます。そのまま、インストールをタップすると、インストールが完了します。
ホーム画面に登録されました。
さっそく、アイコンをタップして起動します。
最初に、スプラッシュ画面が表示された後、めでたく、起動できました。
ブラウザで見た画面と同じで、なおかつアドレスバーもありません。以下のようにアプリとして登録されるので、もうネイティブアプリと区別がつきません。
その他
パスワードマネージャとしての実装は、GitHubをご参照ください。。。
簡単に、使い方だけ。トップ画面です。
先に、右上のAPI Keyから、apikeyを設定します。
それに対応するファイルをあらかじめサーバ側に作成しておく必要があります。
\data\password\****.json
拡張子.json を抜いたファイル名を指定します。サンプルとして、test.jsを置いておきました。これはバレバレなので、乱数の長めの文字列をファイル名にして他人から推測されないようにしてください。
※当然ですが、パスワードが漏洩するなど、私は一切責任を持ちません!
apikeyを変更した場合は、「F5キー」で表示をリロードしてください。
それでは、パスワードを作成しましょう。「新規作成」ボタンを押下します。
ここに、記憶したいまたは作成したいユーザIDやパスワードを入力します。パスワードは、自動生成機能を用意しておきました。
nameやurlの入力は任意です。というより、nameを入れないと、他と区別がつかないです。
最後に、「作成」ボタンを押下します。こんな感じで作成されました。パスワードはサーバ側の先ほどのjsonファイルに保存されています。
Copy列のボタンを押下すれば、パスワードがクリップボードにコピーされます。
変更したい場合は、「変更」ボタンを押下すると、サーバ側に保持した内容を変更します。「変更」ボタンを押下したとき、サーバ側で変更が発生したことを知らせる通知がクライアント側に届くようにしました。
パスワードを削除したい場合は、「削除」ボタンを押下します。
終わりに
以下のページを参考にさせていただきました。
PWAの作り方をサクッと学ぶ - 「ホーム画面に追加」「キャッシュ操作」「プッシュ通知」の実装
https://eh-career.com/engineerhub/entry/2019/10/24/103000PWAのプッシュ通知の仕組み
https://ajike.github.io/how-pwa-push-works/
(っていうより、こちらのページの方が説明が丁寧です。。。)W3C Push API
https://w3c.github.io/push-api/Voluntary Application Server Identification for Web Push (VAPID) (RFC 8292)
https://tools.ietf.org/html/rfc8292以上