20210416のNode.jsに関する記事は7件です。

Electron:ver12のcontextBridgeとipcRenderer.invoke

 本記事では、ElectronのVer.12以降でセキュアにモジュールを使用する際に躓きがちな「ContextBridge.ipcMainWorld」と「ipcRenderer.invoke」について解説します!  さらに、具体例にSlackAPIによるメッセージの送信を使用することで、一緒に使い方を覚えてしまおうという魂胆です! (注意)  非プログラマーの独学&初投稿になりますのでどうぞお手柔らかに。  ここ直すといいよ!というアドバイスいただけたら幸いです! 目次 作成する機能 セキュアにモジュールを使用するには 実際にやってみる SlackAPIでメッセージ送信 まとめ 1.作成する機能  「ボタンをクリックしたら、Slackにあらかじめ決められたメッセージ(かめはめ波)を飛ばす」だけの非常にシンプルな機能を作成します。  という建前のもと、デバックの取り方わからなかったので、代わりにAPIでメッセージを投げます(笑) electronについてはこちら 2.セキュアにモジュールを使用するには electronでは、以前からXSSの危険性が指摘されています。 バージョン更新のたびにデフォルトの設定が変更になるなど、 私を含め初心者にとってややこしい状況かと。 まずは公式ドキュメントを見てみましょう。また併せて日本語でも検索します。 それぞれ共通するキーワードをピックアップします。 preload.js ContextBridge.ipcMainWorld ipcRenderer.invoke ipcMain.handle これらを組み合わせることにより、従来より指摘されていたXSSの危険性を回避しつつ、モジュールを使用することができるっぽいですね。 全体のイメージはこんな感じでしょうか。 3.実際にやってみる まずはじめに、画面となるHTMLを準備します。 test.html <html lang="ja"> <head> <meta charset="UTF-8"> <meta content="width=device-width" name="viewport"> <title>テスト</title> </head> <body> <p><input type="button" value="かめはめ波" id=Button></p> <script src="./renderer.js"></script> </body> </html>  rendererプロセス側から「window.任意のAPIキー.任意のAPI名(引数)」という形でpreload.jsの処理を呼び出します。 renderer.js const button = document.getElementById("Button"); button.addEventListener('click',async () => { const result = await window.electron.sendToMain("かめはめ波"); console.log(result); })  次にpreload.jsでは、「contextBridge.exposeInMainWorld('任意のAPIキー', {任意のAPI名:処理内容})」という形で窓口を用意します。  この時、処理内容には「ipcRenderer.invoke('任意の名前', 引数)」を記述し、mainプロセスの処理を呼びに行きます。 preload.js const {contextBridge,ipcRenderer} = require("electron"); contextBridge.exposeInMainWorld( 'electron', { sendToMain: async (skill) => { const result = await ipcRenderer.invoke('invoke-test', skill) ; return result; } })  最後にmainプロセスでは、preload.jsに対応する「ipcMain.handle('任意の名前',async(event,data) => { 処理内容...}」と記述し、呼び出しの受け取りと処理の実行を行います。 main.js ipcMain.handle('invoke-test', async(event,data) => { const Result = await sendMessageSlack(); return Result }); const sendMessageSlack = async() => { //処理 } SlackAPIでメッセージ送信 SlackのApiの使用に関してはこちら  上記のMainプロセスで呼び出される関数を以下のように記述します。TokenやChannel名は態々外に出さなくてもいいかもですが、後から見てわかりやすいかなと思ってconstしてます。 main.js const { WebClient, LogLevel } = require("@slack/web-api"); const apiToken = "Slackの設定で取得したApiToken"; const chatGroup = "任意のチャンネル名"; const client = new WebClient(apiToken, {logLevel: LogLevel.DEBUG}); const sendMessageSlack = async(text) => { try { const result = await client.chat.postMessage({ token: apiToken, channel: chatGroup, text: text }); console.log(result); } catch (error) { console.error(err); } } まとめ その他の参考記事 所感  初心者にとって、async/awaitが入れ子になっている上に、他では使わない概念の理解やMethodの使用が求められ、なかなか大変でした。  すでに同様の内容を書かれている記事も見ましたが、うまく動かなかったり、 かゆいところに届かなかったりで、自分で試行錯誤して何とかたどり着きました。  もし同じような境遇の方がこれをタタキにして、やりたかったことができると?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node+WebSocket-Nodeで動画データ送信時にEPIPEが発生する

はじめに Node+WebSocket-Nodeでクライアントにある動画ファイル(.mp4)をサーバーに送信するプログラムを書き動かしている際、とある送信タイミングで(クライアント側で)EPIPEが発生したので、その原因と対応内容について記載します。 送信していたデータ クライアントから送信する動画ファイル(.mp4)は以下のようなものでした。 -rwxrwxrwx 1 root root 164514 Apr 16 14:13 /path/data/20210416/14/1311.mp4 -rwxrwxrwx 1 root root 157882 Apr 16 14:13 /path/data/20210416/14/1313.mp4 -rwxrwxrwx 1 root root 266566 Apr 16 14:13 /path/data/20210416/14/1315.mp4 -rwxrwxrwx 1 root root 296955 Apr 16 14:13 /path/data/20210416/14/1317.mp4 -rwxrwxrwx 1 root root 175276 Apr 16 14:13 /path/data/20210416/14/1319.mp4 上記の1311.mp4, 1313.mp4, 1315.mp4の送信は問題なく行えますが、(とある送信タイミング==)1317.mp4を送信するタイミングでEPIPEが発生しています。 サーバー側のソース myServer.js const WebSocketServer = require('websocket').server; const http = require('http'); var server = http.createServer(function(request, response) { console.log((new Date()) + ' Received request for ' + request.url); response.writeHead(404); response.end(); }); server.listen(port, function() { console.log((new Date()) + ' Server is listening on port ' + port); }); var wsServer = new WebSocketServer({ httpServer: server, autoAcceptConnections: false, }); wsServer.on('request', function(request) { if (!originIsAllowed(request.origin)) { request.reject(); return; } var connection = request.accept('echo-protocol', request.origin); connection.on('message', function(message) { }); connection.on('close', function(reasonCode, description) { }); }); クライアント側のソース myClient.js client = new WebSocketClient(); client.connect(wss_server, 'echo-protocol', origin, wss_headers); client.on('connect', function(connection) { console.log('Event : Connected'); connection.on('error', function(error) { console.log("Connection Error: " + error.toString()); }); connection.on('close', function() { console.log('Connection Closed'); }); エラーになった時のクライアント側のコンソールログ クライアント側のコンソールログ Connection Error: Error: write EPIPE Connection Closed 原因 先に書いてしまいますが、原因は送信したデータのサイズが、WebSocket-Node(WebSocketServer)の最大メッセージサイズ(maxReceivedMessageSize)を超えていたためでした。 コンポーネント パラメータ デフォルト値 実際のデータサイズ WebSocketServer maxReceivedMessageSize 0x100000(1MB) 1MB以上??? WebSocketClient maxReceivedMessageSize 0x800000(8MB) - 今回問題となったのは送信時(サーバーが受信した時)ですのでWebSocketServerのmaxReceivedMessageSizeのサイズが影響してきますが、参考までにWebSocketClient のmaxReceivedMessageSizeも載せておきます。サーバーからの送信時(クライアント側で受信した時)にEPIPEが発生した場合は、この辺りを疑ってください。 解決策 WebSocketServerのmaxReceivedMessageSizeを3MBに変更します。(今回のケースでは2MBでも大丈夫だとは思いますが、ひとまず3MBにしました) WebSocketClientの最大受信メッセージサイズ(maxReceivedMessageSize)のデフォルトは8MBです。 なぜWebSocketServerのmaxReceivedMessageSizeのデフォルトは1MBなのか、、、。 Webサーバーとブラウザの関係から来ているのでしょうかね。(Webサーバーにある動画ファイルをブラウザで再生するようなケースを想定しているなど) 対応コード WebSocketServerのconfigにて、maxReceivedMessageSizeに3MB(0x100000 * 3)を追加しました。 myServer.js var wsServer = new WebSocketServer({ httpServer: server, autoAcceptConnections: false, maxReceivedMessageSize: 0x100000 * 3 ・・・ この行を追加 }); maxReceivedMessageSizeに設定する値は10進で 3145728 でもよいですが、WebSocketServerのソースを見ると以下のようになっていましたので、踏襲した形で 0x100000 * 3 としました。 WebSocket-Node:WebSocketServer.js // 1MiB max message size, only applicable if // assembleFragments is true maxReceivedMessageSize: 0x100000, 解決するまでの経緯 参考までに解決するまでの経緯を書きたいと思います。 WebSocket-Node のソースを取得しチェック WebSocket-NodeのGithubにあるREADMEだけでは情報が不足していますのでソースをgit cloneし眺めてみます。EPIPEがヒットしないか検索したり、最大受信メッセージサイズをチェックしたり。 実は最初っから最大受信メッセージサイズ辺りを疑っていました。 ただ最初にも記載しましたが、送信していた動画ファイルのサイズが300KB弱であることと、maxReceivedMessageSizeのデフォルト値が1MBだったので問題ないと判断し、ここではスルーしてしまいました。(最終的にはここにたどり着くのですが。詳細はまた後記) デバッグログの追記 WebSocket-Nodeのソースを眺めても問題なさそうだったので、もう少し挙動を確認するため、ログを埋め込むことにしました。 クライアント側のソース(myClient.js)ではエラーの内容を出力していたのと、コンソールログに"Connection Closed"とも出ていたので、そうなるとクライアント↔サーバー間の接続が切れている==サーバー側でも何かしらログが取れるかも、ということでサーバー側のロジック(myServer.js)を見てみます。 すると、、、 myServer.js connection.on('close', function(reasonCode, description) { }); reasonCode, descriptionでもう少し詳細なログが取れそうなことが分かりました。そこで、、、 myServer.js connection.on('close', function(reasonCode, description) { console.log('disconnected : reasonCode =', reasonCode, ", description =", description); }); として動作確認を行います。すると、、、 サーバー側のコンソールログ disconnected : reasonCode = 1009 , description = Maximum message size exceeded. が出ました。”WebSocket 1009”で検索してもヒットしますが、description にある通りメッセージのサイズオーバーが原因のようです。(あれあれ?やはり動画ファイルのサイズが影響している???) 改めて送信側(myClient.js)のロジックを見直してみると、、、 myClient.jsの送信ロジックあたり fs.readFile(movieFilePath, function(err, data) { if (err) { console.error("Failed to publish : " + err); if (callback) callback(); return; } var content = { "from": myId(), "to": getRequesterId(), "clientType": "client", "request": "send_movie_file", "fileKey": fileKey, "movieIndex": movieIndex, "data": msgpack.encode(data) }; // Websocket通知 wsAgent.sendContent(JSON.stringify(content)); !! データのサイズが1MBを超えたのは msgpack.encode(data) を行っているためですね。 実は動画ファイルのデータだけではなく、その動画ファイルに対する付随情報も送信する必要があったため上記のロジックのようにJSON形式で送信していました。JSONでは動画データ(バイナリ)をそのまま送れないのでmsgpackを利用しています。 BASE64などでもそうですが、msgpackでもエンコードの過程でエスケープ文字などが付加されるため、最終的なデータのサイズは元のサイズより増えます。元は300KB弱の動画ファイルのデータがmsgpack化により1MBを超えてしまったのでしょう。 実際にJSON.stringify(content)のサイズをログに出してみると、、、 JSON.stringify(content)のサイズ data size = 1070254 のようにmaxReceivedMessageSizeのデフォルト値である1MB(1,048,576Byte)に対して20KBオーバーになっていました。 そこで改めてWebSocket-Nodeのソースを再確認し、"対応コード"で記載した設定を追記しました。 するとEPIPEで送信できなかった1317.mp4も無事に送信できるようになりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node(express)のAPIサンプル作成

??概要 Node未経験だった頃(202002) 突然「自力でExpressを使ってAPIを作ってください」と言われた時の作業ログです。 expressを使って、curl実行後「hello world」を返すサンプルを作ります。 前提 node, npmをインストールしていること 環境情報は以下 # os : mac $ node -v v8.9.4 $ npm -v 6.14.4 ?‍♂️レッツトライ 初期プロジェクト作成 bashで実行 # ローカルインストールでもよかったかも $ npm install -g express # 初期プロジェクトを自動生成してくれるgenerator $ npm install express-generator -g # プロジェクト作成 $ express [プロジェクト名] $ cd [プロジェクト名] $ npm install 今回は、「express_sample」というプロジェクトを作りました 不要なファイル削除 tree構成 $ tree ./ ├── app.js ├── bin │   └── www ├── node_modules ├── package-lock.json ├── package.json ├── public │   ├── images │   ├── javascripts │   └── stylesheets ├── routes └── views # 画面無しならpublicとviewsいらない $ rm -rf views $ rm -rf public # routerは自分で作るので削除 $ rm -rf routes/* 実装 【手を付けるファイルは以下】 ・bin/www サーバ起動してapp.jsを呼び出すファイル。今回は修正必要なし   ・APIのポート番号を変えたい場合はprocess.env.PORTを指定する必要あり ・app.js リクエスト受信時に、エンドポイントごとにroutingするモジュール app.js var express = require('express'); var testRouter = require('./routes/hello_test'); var app = express(); app.use(express.json()); app.use('/sample/v1/test/', testRouter); module.exports = app; -> localhost:3000/sample/v1/test/にアクセスすると、hello_test.jsの処理を実行される リクエストGETする処理↓ routes/hello_test.js var express = require('express'); var router = express.Router(); // ルーティング元から'/'を受け取ったら、'hello world'を返す router.get('/', function(req, res, next) { res.send('hello world'); }); module.exports = router; 実践! # API側:サーバ起動 $ node ./bin/www # 外からcurl $ curl -s localhost:9001/sample/v1/test/ hello world ディレクトリ構成 いろいろ消したり足したり試行錯誤した結果、以下の構成にしました。 $ tree ./ ├── app.js ├── bin │   └── www ├── config # 設定値・定数 ├── node_modules ├── routes # ルーティングの受け口 ├── src │   ├── action # APIの処理  │   ├── lib # Utilなど │   │   └── db # sequelize関連のモジュールは全部ここ │   └── batch # バッチ ├── package-lock.json └── package.json
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ESlint+VScodeにスタイルガイド追加

概要 なにをしたのか 以下のコーディング規約を元に、VSCodeでソースの静的解析&自動フォーマットをできるようにした インデントはspace4文字 文末のセミコロン必須 ""使用不可 他のフォーマットは「Felix's Node.js Style Guide」に従うこと ※用語メモ eslint:JavaScriptの静的解析ツール。NodeだけでなくReactなどでも使えるはず Felix's Node.js Style Guide:ベルリンのプログラマFelixさんのスタイルガイド。Node.jsのメジャーな規約のひとつ。 前提 Node.js, npmをインストール済であること nodeのプロジェクトを作成済であること 本題:作業ログ 事前準備:eslint導入 インストール command # eslintインストール $ npm install eslint # 初期設定:質問に答えるだけ $ npx eslint --init ? How would you like to use ESLint? To check syntax, find problems, and enforce code style ? What type of modules does your project use? CommonJS (require/exports) ? Which framework does your project use? None of these ? Does your project use TypeScript? No ? Where does your code run? Node # ブラウザ使わない ? How would you like to define a style for your project? Answer questions about your style ? What format do you want your config file to be in? JSON ? What style of indentation do you use? Spaces # インデントはスペース ? What quotes do you use for strings? Single # Stringの区切りは""ではなく'' ? What line endings do you use? Unix # CRLF or LF ? Do you require semicolons? Yes # セミコロン必須 ↓.eslint.jsonが作られる VScode設定 拡張機能インストール 設定完了。超怒られてる これでeslintの導入は完了です! 本題:eslintにスタイルガイド追加 felixスタイルガイドの設定ファイルがgithubに上がっているのです・・・知らなかった・・・・ https://github.com/felixge/node-style-guide command # clone $ git clone https://github.com/felixge/node-style-guide.git $ ls -a . .editorconfig .jshintrc index.js .. .eslintrc Readme.md package.json # 以下のファイルをプロジェクト配下に移動 $ cp -p ./{.eslintrc,.editorconfig} [指定のディレクトリ] eslintに反映 .eslintrcの中身を、さっき作った.eslintrc.jsonに置き換える。 設定上の注意 const,letやアロー関数等を使う場合は、es6をtrueにすること async/awaitを使う場合はparserOptions以下も足す あとはよしなにカスタマイズする。 今回はindent、linebreak-styleを足しました .eslintrc.json { "env": { "node": true, "es6":true }, "parserOptions": { "sourceType": "module", "ecmaVersion": 2018 }, "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "rules": { "indent": ["error",4], "linebreak-style": ["error","unix"], "array-bracket-spacing": [2,"never"], "block-scoped-var": 2, "brace-style": [2,"1tbs"], "camelcase": 1, "computed-property-spacing": [2,"never"], "curly": 2, "eol-last": 2, "eqeqeq": [2,"smart"], "max-depth": [1,3], "max-len": [1,80], "max-statements": [1,15], "new-cap": 1, "no-extend-native": 2, "no-mixed-spaces-and-tabs": 2, "no-trailing-spaces": 2, "no-unused-vars": 1, "no-use-before-define": [2,"nofunc"], "object-curly-spacing": [2,"never"], "quotes": [2,"single","avoid-escape"], "semi": [2,"always"], "keyword-spacing": [2, { "before": true, "after": true } ], "space-unary-ops": 2 } } VScodeに反映 EditorConfig for VS Codeを使います * editorconfigをいいかんじに設定する * 今回はindent_sizeをspace2 -> 4に修正しました .editorconfig # editorconfig.org root = true [*] charset = utf-8 insert_final_newline = true trim_trailing_whitespace = true end_of_line = lf indent_style = space indent_size = 4 これでOKなはず!意外と楽にできました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Native + Expo CLI 環境構築 Mac編

Node.jsのインストール Node.jsのバージョン管理をするためnodebrewをインストールする brew install nodebrew インストール可能なnode.jsのバージョンを確認 nodebrew ls-remote 指定のバージョンのインストール nodebrew install (version番号) 最新バージョンのインストール nodebrew install latest ※最新バージョンインストール時に、No such file or directoryのエラーが出た。実際にディレクトリがなかったので作成し、再度インストールすることで対応。 expoのインストール npm i -g expo-cli 続いて、プロジェクト作成 expo init (プロジェクト名) 注意 インストール後に環境パスが通っていない場合、「node -v」がnode not foundと出るのでパスを通す必要がある。 echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.zprofile 実行 Xcodeのシミュレータを使うので、Xcodeがインストール済みが前提です。 expo start 以下のように表示されればOK
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

`preinstall: typesync`がgit clone直後にエラーになるのを防ぐ

はじめに npm scriptsにpreinstallを書いておくと、npm installとかyarn addの前に自動でスクリプトを実行してくれます。 TypeScriptで開発する場合、typesyncをpreinstall内で流すと、インストール時に@types/のパッケージがあれば自動インストールしてくれて便利です。 { "scripts": { "preinstall": "typesync" } } 問題点 preinstallが走る時点でtypesyncがインストールされていないと、スクリプトの実行がエラーになり、続くインストールが止まってしまいます。 そのため、typesyncがdevDependenciesに含まれてるプロジェクトをgit cloneしてきた場合、clone直後のnpm installは失敗しちゃうんですね。 単独でtypesyncだけ先に入れるとか、そもそもグローバルインストールしておけば解決しますが、ローカルインストールでもgit clone && npm installが普通に成功する方法があります。 解決策 { "scripts": { "preinstall": "typesync || true" } } または { "scripts": { "preinstall": "typesync || :" } } true は何もせず、終了ステータスとして'成功'を意味する 0 を返す。 本コマンドはシェルスクリプト中で正常終了するコマンドが必要な場合に 使われる。なお,シェルのビルトインコマンド ':' (コロン)は 同じ動作をより速く実行する。 スクリプト全体の終了ステータスが0であればインストールが実行されるので、シェル芸でcommand || trueすればcommandが失敗してもインストールは続くわけです。try-catchでエラーを握りつぶす感覚に近いですね。 typesyncに関しては、clone直後のインストールであれば、その時点で@types/が新規追加されることはなくて、package.json内の全パッケージが入ればOKですからね。 もちろん、typesync以外ではちゃんと異常終了を検知すべき場合も多いので、やみくもにエラーを握りつぶさないようには気をつけましょう。 では今日はこのへんで!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

homebrewでnodenvをサクッとインストール

前提条件 いづれもインストールされていない状態を想定 $which yarn $which node $which npm homebrewでNode.jsをインストール $brew install nodenv $nodenv -v nodenv 1.4.0 bash_profileに追記 $echo 'export NODENV_ROOT="$HOME/.nodenv"' >> ~/.bash_profile $echo 'export PATH="$NODENV_ROOT/bin:$PATH"' >> ~/.bash_profile $echo 'eval "$(nodenv init -)"' >> ~/.bash_profile 再起動 $source ~/.bash_profile nodenvを利用してNode.jsをインストールとセットアップ $nodenv install 14.15.0 $nodenv global 14.15.0 $node -v v14.15.0 $npm -v 6.14.8 $which node /Users/satoru/.nodenv/shims/node
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む