Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all 8814 articles
Browse latest View live

AWS Lambda関数でNode.js レイヤーを作る

$
0
0

対象者

AWSでLamdbaを使っている人/どんなことができるのか調べている人
LINEBotをサーバーレスで作りたい人
Lambdaのレイヤーという機能を知らない人/興味ある人

下準備

とりあえずデスクトップに移動
$ cd Desktop
ディレクトリ作る
$ mkdir nodejs
nodejsディレクトリに移動
$ cd nodejs
初期化
$ npm init -y
レイヤーにしたいパッケージをインストール
$ npm i xxxxx
ex)LINEBotのSDKをインストールする場合
$ npm i @line/bot-sdk
nodejsディレクトリ自体をzipファイルに圧縮して下準備完了

AWSコンソール

ログイン→Lambda→レイヤー→レイヤーの作成
スクリーンショット 2020-04-21 16.53.40.png
名前はそのパッケージが分かる名前ならなんでもいい。
↓例えばの完成図
スクリーンショット 2020-04-21 16.55.11.png

Lambda関数で読み込む

index.js
constline=require('@line/bot-sdk')

こんな感じでレイヤーを使うことができます!
毎回全ファイルをZipしてアップロードしている人には超おすすめです(これを知ってから超楽になった。)


Box APIを使い始める。JWT認証でBOXにつないでみる。

$
0
0

はじめに

Box APIの使いかたについて、簡単に導入方法を紹介します。

Box APIを利用するための前提

前提として、BOXテナントの管理者権限を持っていることを前提としています。
この記事ではBox Node SDKを利用しますが、基本の考え方は他のSDKを利用する場合、またはSDKを使わずに、REST APIを直接実行する場合でも共通しています。

リソース

参考にしたリソースはこちら。

Box DEV
https://developer.box.com/
API、各種SDK等、開発に関して様々な情報がまとまっています。

Box Support
https://support.box.com/hc/en-us
Boxの標準環境での使いかたなどがのっています。

どちらも、英語版の他、日本語版が用意されています。

BOX側でのアプリの作成(開発の事前準備)

Box側でアプリ作成

  1. Box DEVを開いて、マイアプリをクリックします。
    https://ja.developer.box.com/
    まだBoxにログインしていない場合は、ここで管理者としてBoxにログインしてください。この操作でBoxの画面の左ナビゲーションの中に「開発者コンソール」というリンクが表示されるようになります。

  2. 開発者コンソールを開きます

  3. アプリの新規作成をクリックします

  4. アプリの種類を聞かれるので、「カスタムアプリ」を選択します。
    このアプリの種類は、アプリ作成のウィザードを選択しているだけです。あとから設定を変更することでアプリのタイプを変えることができます。何も考えずにカスタムアプリで大丈夫です。

  5. 認証方法の選択をします。ここでは「JWTを使用したOAuth 2.0(サーバー認証)」を選択します。
    このガイドではBox Platformの中心的な認証方法である、JWT方式で説明していきます。

  6. アプリケーションの名前をつけます。

  7. アプリが作成されるので、アプリの表示ボタンを押します。
    この画面で表示されているcurlのコマンドの使用例は単なる例なので、無視して次に進んでください。

  8. 作成したアプリの構成画面が表示されます。以下ざっくりと説明です。

  • Developerトークン
    • 正規の認証を経なくとも、このDeveloperトークンを利用することでAPIを試すことができます。
    • 60分しか使えないので、再発行する必要があります。
    • 今回は利用しません。
  • OAuth2.0資格情報
    • クライアントIDはあとでアプリケーションを承認する時に必要になります。
    • 公開キーの追加と管理で「公開/秘密キーペアを生成」を押した時に作られるConifgファイルにこれらの情報が含まれます。
  • アプリケーションアクセス
    • とりあえずデフォルトのままで。
    • 既存ユーザーとコンテンツにアクセスする必要があればEnterpriseを選びます。
  • アプリケーションスコープ → 一旦デフォルトのままにします。
  • 高度な機能 → 一旦デフォルトのままにします。
  • 公開キーの追加と管理
    • こちらで公開/秘密キーペアを生成します。
    • 「公開/秘密キーペアの生成」をクリックすると、configファイルがダウンロードされるので後で利用します。config.jsonとリネームしておきます。
    • git等で共有しないようにします。このファイルの中身は、プロダクション環境では環境変数等に保存するようにするのがおすすめです。

Box側でアプリの承認

  1. マイアプリ > 一般 > アプリの承認で、承認用に送信ボタンを押します。

    • クライアントIDが表示されるのでコピーしておきます。
    • または、マイアプリ > 当該アプリ > 構成 > OAuth2.0資格情報 のクライアントIDをコピーします。
    • 管理コンソールにアクセスできるのであれば、この送信ボタンを押す必要はありません。
  2. 管理コンソール > アプリ > カスタムアプリ・タブを開き、 「新しいアプリケーションを承認」ボタン押して、コピーしたクライアントIDをペーストし、承認します。

    • ここで、構成の中で指定した各種スコープ等を確認されるので、承認ボタンを押します。

これでBOX側の準備は最低限ととのいました

サンプルコードの作成手順

ここからはカスタムアプリケーションの作成です。

前提

  • node.jsが導入済みで、nodeコマンドが実行できること

  • 筆者の環境では、nodeのバージョンは、v12.16.1を利用しています。

  • npmまたはyarnが導入済みであること

nodeプロジェクトの作成

# 任意の名前でフォルダを作成し、入ります。mkdir box-quick-start 
cd box-quick-staret 
# nodeのパッケージの初期化をします。yarn / npmお好みで。
yarn init -y

Box-node-sdkを追加

# box-quick-staretフォルダで実行。box-node-sdkを導入します
yarn add box-node-sdk 

参考:SDKはこれを利用しています。 https://github.com/box/box-node-sdk

生成されたpackage.json

package.json
{"name":"box-quick-start","version":"1.0.0","main":"index.js","license":"MIT","dependencies":{"box-node-sdk":"^1.32.0"}}

ダウンロードしておいたconfig.jsonをコピー

box-quick-staretフォルダにconfig.jsonをコピーします。
プロダクション環境ではこのファイルの内容が漏洩しないように気をつけてください。

ファイルを追加

# box-quick-staretフォルダで実行。ファイルを追加します。touch index.js

Index.jsの編集

任意のエディタを開き、以下のコードをペースト

index.js
constboxSDK=require("box-node-sdk");// SDKですconstconfig=require("./config");// config.json: Boxからダウンロードしたファイルです。constmain=async()=>{// config.json を元に、sdkを作成。constsdk=awaitboxSDK.getPreconfiguredInstance(config);// Service AccountのClientオブジェクトを取得。// 管理者ユーザーとは別。管理者ユーザーのコンテンツはこのままでは見れないので注意。// Service Account/App Userのどちらでもコンテンツを操作可能だが、// 複数ユーザーで利用する場合はコンテンツの所有者や変更履歴の明示化のためAppUserを作成することが望ましい。constsaClient=awaitsdk.getAppAuthClient("enterprise");// App Userを作成する。本来は再利用のために、戻ってきたappUserのIDを保存する必要がある。constappUser=awaitsaClient.enterprise.addAppUser("Mike Morris");// 作成したAppUserのスコープでClientを作成constauClient=awaitsdk.getAppAuthClient("user",appUser.id);// アップロードのサンプル。// package.jsonを、AppUserのホームフォルダにアップロードしてみるconstfs=require("fs");conststream=fs.createReadStream("./package.json");// AppUserでホームフォルダにファイルをアップロードconstfile=awaitauClient.files.uploadFile("0","package.json",stream);// アップロードされたファイルの情報を確認console.log(file);// AppUserのホームフォルダを一覧constauItems=awaitauClient.folders.getItems("0");// アップロードしたpackage.jsonがリストに入っていることを確認console.log(auItems);};main();

上記ファイルの実行方法

# box-quick-staretフォルダで実行
node index.js

まとめ

以下のことが簡単に実現できました。

  • box-node-sdkの導入方法と、JWT認証の利用の仕方
  • Service Accountの利用の仕方
  • App Userの作成の仕方
  • ホームフォルダ以下のコンテンツの一覧の取得方法

この他様々な操作は、以下のbox-node-sdkと、API Referenceを調べることで使いかたはすぐにわかると思います。

box-node-sdk: https://github.com/box/box-node-sdk

API Reference: https://developer.box.com/reference/

以下、認証方法に関してのおまけ情報です。

BOXの認証方法について

カスタムアプリケーションからBOXにAPIで接続する場合、基本的に以下の2通りの方法があります。(厳密にいうとWeb統合やBox Skillsの方式の認証などもありますが、ここでは割愛します。)

  • 標準OAuth 2.0 (ユーザー認証)
  • JWTを使用したOAuth 2.0(サーバー認証)

2つの認証方式には、以下のような特徴があります。

標準OAuth 2.0 (ユーザー認証)

  • https://ja.developer.box.com/guides/authentication/oauth2/

  • Box Devガイドにかかれている、OAuth 2.0を使用する最適なパターン

    • 既存のBoxアカウントを持っているユーザーを使用する
    • ユーザーにBoxを使用していることを知らせる必要がある
    • ユーザーのBoxアカウントにデータを保存し、アプリケーションのBoxアカウントには保存しない
  • 注意:BoxにはFair Use Policyというものがあり、実際に人間が使っていないアカウント(ロボットやプログラムのみが利用)が、この認証方式を利用してはいけないとされている。 https://cloud.box.com/s/cotkk9k3op6zw07rmr6jb3mhgv2dix4a

JWTを使用したOAuth 2.0(サーバー認証)

  • https://ja.developer.box.com/guides/authentication/jwt/
  • Service Accountと呼ばれるAPI専用の管理者のようなユーザーに対して認証を行う。バックエンドサーバー内で認証しておく。
  • 一度Service Accountが認証されると、以降はService Accountを利用して、App Userと呼ばれる、利用者用アカウントをBoxの管理対象ユーザーとは無関係に自由に好きなだけ払い出すことができる。BoxはService Accountだけを認証し、App Userの認証を行わない。
  • この方式を利用し、個々のユーザーに利用させるパターンにする場合は、App Userとカスタムアプリのユーザーのマッピングを独自に管理する必要がある
  • バックエンドサーバー内から認証されることを想定(技術的には可能だが、強力な権限を持つService Accountのパスワードやアクセストークンが漏洩してしまうので、ブラウザ上だけでこの方式の認証を行うべきではない)
  • Box Platformというプラン体系の中心的な位置づけの認証モデル。上記のFair Use Policyは適用外。
  • Box Devガイドにかかれている、JWT認証を使用する最適なパターン
    • Boxアカウントを持たないユーザーを使用する
    • 独自のIDシステムを使用する
    • ユーザーにBoxを使用していることを認識させたくない
    • アプリケーションのBoxアカウントにデータを保存し、ユーザーのBoxアカウントには保存しない
  • ガイドの説明以外で、JWT認証が活用できそうなパターン
    • 業務に特化した専用UIを持つカスタムアプリケーションを作成し、コンテンツをBOXに預ける。
    • メールアドレスを持っていない人たち向けのカスタムアプリケーションを作成
    • バッチプログラム等の専用のアカウントとして利用。
    • 既存サービスから利用することで、Boxを内部で利用しつつ、既存Saasの販売を行う。

APIの実行、SDKの利用ともに、認証方法はどちらでも構いません。

この記事では主にJWT認証を利用した方法を説明しています。

参考: https://ja.developer.box.com/guides/authentication/jwt/with-sdk/

セマンティックバージョニングを正規表現で評価する

$
0
0

公式リンク↓↓

セマンティックバージョニング2.0.0(最後の方に載ってます)

見るのが面倒な人向け↓↓

constregex=newRegExp(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/);if(regex.test(hogehoge)){console.log("OK")}

Linux環境におけるNode.jsの導入から利用

$
0
0

はじめに

投稿者の備忘録としての活用が主です。
Webの作成において役に立つNode.jsを導入します。

環境

wsl(Windows Subsystem for Linux)にインストールします。

Editor: VSCode
Shell: bash version 4.4.20
Ubuntu: 18.04.4 LTS

目次

  1. nvmの導入>>
  2. Node.jsの導入>>
  3. REPLを使ってみる>>
  4. ファイルを実行してみる>>

1.nvmの導入

Node.jsのバージョンを管理するためにまずnvmを導入します。
これにより、現在利用しているNode.jsのバージョンの把握や
別バージョンへの切り替えなどを行えます。

wsl
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash

終了したら、

wsl
$ source ~/.bashrc 

で、.bashrcの内容を読み込んでおきます。

wsl
$ nvm

Node Version Manager

Note: <version> refers to any version-like string nvm understands. This includes:
  - full or partial version numbers, starting with an optional "v"(0.10, v0.1.2, v1)
  - default (built-in) aliases: node, stable, unstable, iojs, system
  - custom aliases you define with `nvm alias foo`以下略。。。

となれば、インストールが成功しています。

2.Node.jsの導入

今回、Ver. 10.14.2 のNode.jsをインストールしました。

wsl
$ nvm install v10.14.2
Downloading and installing node v10.14.2...
Downloading https://nodejs.org/dist/v10.14.2/node-v10.14.2-linux-x64.tar.xz...
###################################################################### 100.0%Computing checksum with sha256sum
Checksums matched!
Now using node v10.14.2 (npm v6.4.1)
Creating default alias: default -> v10.14.2
$ nvm use v10.14.2
Now using node v10.14.2 (npm v6.4.1)
$ node --version
v10.14.2

指定したバージョンのNode.jsがインストールされていることを確認しました。

3.REPLを使ってみる

Node.js版のコンソールだと思えばいいと思います。
(PythonのPythonコンソール的な感じ)
Ctrl+c二回でREPLを終了できます。

REPL
$node
>1+1
2
>(To exit, press ^C again or type .exit)
>

4.ファイルを実行してみる

プログラムの書き方は、JavaScriptと同じかきかたをすればいいと思います。
今回単純な足し算プログラムを書いて動かします。

ソースコード

sum.js
'use strict';functionaAdd(num){varres=0;num[0]=0;num[1]=0;for(letsofnum){res+=parseInt(s);}console.log(res);}aAdd(process.argv);

実行結果

wsl
$ node sum.js 1 2 3 4
10
$ node sum.js      
0

雑に解説

これは、引数の総和をとるプログラムでした。
process.argvに命令がリストとして入っています。
$ node sum.js 1 1 1 2 3を実行したとすると、
リストの中身は、

[ '/home/yosse95ai/.nvm/versions/node/v10.14.2/bin/node',
  '/home/yosse95ai/sum.js',
  '1',
  '1',
  '1',
  '2',
  '3' ]

といった具合になっています。

だから、

num[0] = 0;
num[1] = 0;

の部分で数字以外の文字列部分(パス部分)を、0に置き換えてます。
多分もっとクレバーなやり方があると思います。

おわりに

今回は、かなり初歩的な解説でした。
まだ自分が初心者なので、頭の整理を兼ねて書きました。
おそらく拙い文章でしたが、お付き合いいただきありがとうございました。
さよなら:wave:

参考

関連記事

~Coming soon~

Linux環境におけるNode.jsのためのyarn導入から利用

$
0
0

はじめに

投稿者の備忘録としての活用が主です。
Webの作成において役に立つNode.jsのパッケージマネージャーとして
yarnを導入しました。(Node.jsの導入はこちらから)
(Pythonでいうところのpip的な感じ)
yarnを使って様々なライブラリなどを自分のプログラミングに活用していけます。

環境

wsl(Windows Subsystem for Linux)にインストールします。

Editor: VSCode
Shell: bash version 4.4.20
Ubuntu: 18.04.4 LTS
node: v10.14.2

目次

  1. yarnの導入>>
  2. yarnについて>>
  3. npmパッケージ作成>>
  4. 作成したパッケージの利用>>

1.yarnの導入

wslのコンソールを開き、

wsl
$ curl -o--L https://yarnpkg.com/install.sh | bash -s----version 1.21.1
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     

中略
> Successfully installed Yarn 1.21.1! Please open another terminal where the 
`yarn`command will now be available.

最後の一行が表示されればインストール完了です。

次に、.bashrcファイルを読み込むか、VSCodeを立ち上げなおします。

wsl
$ source ~/.bashrc

2.yarnについて

詳しい使い方(CLI)は公式サイトでみてほしいです。

yarnによるnpmパッケージのインストールには、
グローバルインストール:Node.js実行環境全体で使う
ローカルインストール:カレントディレクトリ内でのみ使う
の二種類ありますが、今回試していくのはすべてローカルインストールであると
理解してください。

3.npmパッケージの作成

このパッケージは、フィボナッチ数列とトリボナッチ数列を出力するパッケージです。
どのような場面で活躍するかな知りません。
あくまでパッケージ作成の練習です

〇パッケージを作成するディレクトリを作成

wsl
$ mkdir trib
$ cd trib 

以下tribディレクトリで作業を行います。


〇npmパッケージ作成のチュートリアル

今回は特に変更などしません。

wsl
$ yarn init
yarn init v1.21.1
question name (trib):

Enterを押下して、パッケージ名をtribにしました。

question version (1.0.0):

Enterを押下して、バージョンを 1.0.0にしました。

question description:

Enterを押下して、パッケージの説明をつけませんでした。

question entry point (index.js):

Enterを押下して、ライブラリとして読み込まれるファイル名を
index.jsにしました。

question repository url:

Enterを押下して、Gitリポジトリは公開しません。

question author:

Enterを押下して、npmの著者名は設定しませんでした。

question license (MIT):

Enterを押下して、MITライセンスに従うものとしました。

question private:

Enterを押下して、

success Saved package.json
Done in 3.45s.

これでパッケージの内容が書き込まれたpackage.jsonというファイルが作成されました。

wsl
$ ls
package.json
$ cat package.json
{"name": "trib",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT"}

〇パッケージtribの実装

任意の方法(touchとか)でtribディレクトリ内にindex.jsを作成し、
実際に実装していきます。

index.js
'use strict';// フィボナッチ数列functionprintFib(length){console.log(length+'番目までのフィボナッチ数列');constmemo=newMap;memo.set(0,0);memo.set(1,1);functionfib(n){if(memo.has(n)){returnmemo.get(n);}constvalue=fib(n-1)+fib(n-2);memo.set(n,value);returnvalue;}for(leti=0;i<length;i++){console.log(fib(i));}}// トリボナッチ数列functionprintTrib(length){console.log(length+'番目までのトリボナッチ数列');constmemo=newMap();memo.set(0,0);memo.set(1,0);memo.set(2,1);functiontrib(n){if(memo.has(n)){returnmemo.get(n);}constvalue=trib(n-1)+trib(n-2)+trib(n-3);memo.set(n,value);returnvalue;}for(leti=0;i<length;i++){console.log(trib(i));}}module.exports={printFib,printTrib};

関数の内容はさておき...(すみません)

module.exports = { printFib, printTrib };

この部分で、module.exportsオブジェクトのプロパティとして関数を登録します。
これはプロパティ名と値の変数名が同一である場合の略記法なので、
プロパティ名と値の変数名が異なる場合などは、

module.exports = {
  printFib: printFib,
  printTrib: PrintTrib
};

のように書いてください。

4.作成したパッケージの利用

パッケージを利用する(インストール)するための、
ディレクトリを作成します。 (∵ローカルインストール)

wsl
$ cd ..
$ mkdir trib-run
$ cd trib-run/

以下、trib-runディレクトリで作業を行います。


先ほど同様に、チュートリアルを行います。

$ yarn init
yarn init v1.21.1
以下略

完了したら、先ほど作成したパッケージをインストールします。

wsl
$ yarn add 
../trib
yarn add v1.21.1
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ trib@1.0.0
info All dependencies
└─ trib@1.0.0
Done in 0.28s.

色々メッセージがでて、インストール完了です。

任意の方法(touchとか)でtrib-runディレクトリ内にsample.jsなどの
JSファイルを作成し、実際に活用していきます。

sample.js
'use strict';constalgo=require('trib');algo.printFib(10);algo.printTrib(10)

それでは、実行してみます。

wsl
$ node sample.js
10番目までのフィボナッチ数列
0
1
1
2
3
5
8
13
21
34
10番目までのトリボナッチ数列
0
0
1
1
2
4
7
13
24
44

以上のことから、パッケージのインストールと利用を行えました。

おわりに

まだ自分が初心者なので、頭の整理を兼ねて書きました。
知識の更新があり次第、こちらに追加していくつもりです。
まだまだNode.js, yarnに関して浅学なので、内容の薄さはご了承ください。
Gitとの関係などもしっかり理解していこうと思っています。

yarnのネコかわいい...
さよなら:wave:

関連記事

参考

Node.jsでBルートから消費電力を取得する

$
0
0

必要なもの

  • BルートのIDとパスワード
  • Wi-SUNの通信デバイス
  • プログラムを実行するPCなど

BルートのIDとパスワード

契約している電力会社に申請します。
僕の場合は東電エリアなので以下のURLから申請を行いました。

2週間ほどで書面にいてBルートに接続するためのIDが送られてきます。
パスワードはメールにて送付されますので、IDと併せて大切に保管しましょう。

Wi-SUNの通信デバイス

僕はテセラテクノロジーのRL7023(8,000円 [税抜])を購入しました。
USBタイプのものは他にもいくつか製品がありますが1万円を超えるものばかりなので、この製品が最も安価だと思います。

https://www.tessera.co.jp/rl7023stick-d_ips.html

プログラムを実行するPCなど

通信デバイスがUSBドングルタイプなのでPCでもMacでも何でも良いですが、僕は手元にRaspberry Pi 3Bが余っていたのでこれを使いました。

OSはRaspbianです。
Node.jsを入れたり、npmでモジュール追加したりするのでインターネットに繋がるくらいにはセットアップしておきます。


開発

Node.js

apt-getするとv10が入ったので、最新安定版のv12を使うためにnodenvを使うことにします。
nodenvからv12.16.2をインストールしました。

RL7023をRaspberry Piにセット

普通に挿しただけで使えるようになります。
シリアルポートを確認するには以下のようなコマンドで確認してみましょう。

pi@raspberrypi3:~ $ ls-l /dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_DJ00RTB4-if00-port0
lrwxrwxrwx 1 root root 13  4月 21 17:17 /dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_DJ00RTB4-if00-port0 -> ../../ttyUSB0

この場合は/dev/ttyUSB0を使います。

Node.jsでシリアルポートに接続

serialportというモジュールを使います。

importSerialPortfrom'serialport'constport=newSerialPort('/dev/ttyUSB0',{baudRate:115200,});

そして

port.once('open',async()=>{// Bルート認証パスワード設定port.write(Buffer.from('SKSETPWD C <Bルートのパスワード>\r\n'))// Bルート認証ID設定port.write(Buffer.from('SKSETRBID <BルートのID>\r\n'))}

というようにシリアルポートにメッセージを送って応答を受け取っていくのが基本になります。

積算電力量計測値履歴1

当日から99日前までの任意の1日の積算電力量を30分区切りで取得できます。
積算電力量なので、過去から未来にかけて増えていくものですが、例えばとある日の0時0分~0時30分時点の積算電力量を次の区切りである0時30分~1時00分の積算電力量から減算すれば、0時0分~0時30分の間で消費した電力がわかります。

こうして見える化できれば、電気代の高い昼間に沢山電気を使っていることがわかるので、洗濯機や掃除機の使用は昼間ではなく朝夕に変えようかな、といった具体的な行動に移すキッカケになると思います。

特に今は緊急事態宣言が発布されているので、在宅勤務をしている人も多いのではないでしょうか。
となると必然的に日中はPCをつけっぱなしになったりするわけで、平時よりも電気代は高くなっているはずですよね。

作ったソースコードは以下のリポジトリに置いてあります。
https://github.com/chaki1019/broute-js

今後

データを取得することができたので、今後は貯めて可視化することを狙っていきます。
貯める場所についてはまだ決めていませんが、クラウドのマネージドサービスを使いたいなあと思ってはいます。

最有力なのはAmazon DynamoDBかな。


参考

ここにある公式なドキュメントが参考になります。
https://echonet.jp/spec_v112_lite/

今回Bルートをやろうと思った背景などは別途ブログに書いています。
もし良かったら見てやってくださいませ。

https://campstylist.com/2020/04/21/broute1/

Firebase FunctionsのエミュレーターでもGoogle API用のアクセストークンを得る方法

$
0
0

Firebase、いつの間にかとても練れた内容になってきてました。
というわけでバリバリ使っておりますが、たまにドハマリし、かつ英語で検索してすら情報ほぼゼロ、ということがあります。
そんな中の「え、これでいいの?」をご紹介します。
今回の内容はタイトルそのまんま、です。

概要

  • Firebase Cloud Functions内で、Google API用のアクセストークンを得たい
    • クライアントサイドでFirebase使いまくりのSPAがあり、そのSPA内からGoogle APIサービスを叩きたい
    • が、SPA内にAPIキーを埋め込みたくない
    • ので、アクセストークンをFunctions内で得、それをSPAに渡したい
  • 超素朴実装をすると、デプロイした実クラウド上ではちゃんと動作するが、ローカルPC上で動作させたエミュレーター上では動作しない
  • ものすごく簡単な解決方法があるが、あまりネット上には出ていない模様

レギュレーション

  • Firebaseプロジェクト作成済み
    • 管理者認証情報作成済み&配置済み&環境変数で指定済み
    • プロジェクトに紐付いた、利用したいGoogle APIを有効化済み
  • Firebase Cloud Functions およびローカルPC上のエミュレーター
    • Functions用の index.jsadmin.initializeApp()を呼び出し済み
  • Node v8

実クラウド上では動作するがエミュレーターでは動作しないコード

index.js
exports.getAccessToken = functions.https.onCall(async (data, context) => {
  // カスタムクレームによる権限認証をここで行う
  try {
    const response = JSON.parse(await request({
      url: 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token?scopes=https://www.googleapis.com/auth/cloud-platform',
      headers: {
        'Metadata-Flavor': 'Google'
      }
    }));
    if (!response.access_token) {
      throw new Error('bad response ' + JSON.stringify(response || {}));
    }
    return response;
  } catch (e) {
    throw new functions.https.HttpsError('aborted', e.message);
  }
);

要は Google CloudのIDとアクセストークンの取得に出ているのをそのままNodeに翻訳しただけです。そしてこれで、実クラウドではきちんと動きます。

しかし、エミュレーター上ではこれは動作しません。

getaddrinfo ENOTFOUND metadata.google.intenral

…そりゃするわけないわ! http://metadata.google.internal/ってどんなFQDNだよ!! っていう単純な話です。これは有名なメタデータサーバー、GCEインスタンスから参照できるアドレスであり、 FunctionsだってGCE(的ななにか)の上で動いているんだから参照可能だけどローカルでは参照できるわけないだろヴォケ、です。

解決策

index.js
exports.getAccessToken = functions.https.onCall(async (data, context) => {
  // カスタムクレームによる権限認証をここで行う
  try {
    return await admin.app().options.credential.getAccessToken();
  } catch (e) {
    throw new functions.https.HttpsError('aborted', e.message);
  }
);

え、これでいいの…? というワンライナーになってしまいました(^^ゞ

実はFirebase Admin SDKの admin.credential.Credentialクラスに getAccessTokenっていうそのまんまのメソッドが生えていて、これを呼び出せば、先述の実クラウドで動作するものと同じフォーマットのJSONが得られます。

そして、管理者認証情報つきで初期化済みのAdmin SDKの Appインスタンスは、optionsプロパティーの中に credentialプロパティーを抱えているわけです。

謎(調べてないだけ)

しかしこれ、Functionsエミュレーターはどうやってエミュレートしてるんですかね、メタデータサーバーを…何か秘密の口がFirebase側に生えてるのかしら?

Node.js + ibm_db + Db2 で、ODBCパラメータを設定する

$
0
0

Node.jsのアプリケーションを実行にあたって、ODBCパラメータを設定することができます。
ODBCパラメータはdb2dsdriver.cfgファイルに設定します。

db2dsdriver.cfg ファイルの作成

Db2 9.7 FP3 以降、db2dsdriver.cfg 構成ファイルはDb2には同梱されない代わりにサンプルファイルが同梱されるらしいです。(db2dsdriver.cfg.sample ※)
 (※)Docker版Db2には、サンプルファイルも入っていないかも?

<configuration>
   <dsncollection>
      <dsn alias="DB1" name="TESTDB" description="alias_db1_description" host="localhost" port="50000"/>
      </dsn>
   </dsncollection>
   <databases>
      <database name="testdb" host="localhost" port="50000">
         <parameter name="CurrentSchema" value="db2inst1"/>
         <specialregisters>
            <parameter name="CURRENT DEGREE" value="'ANY'"/>
         </specialregisters>
      </database>
   </databases>
</configuration>

DB2DSDRIVER_CFG_PATH レジストリー変数に、db2dsdriver.cfg のパス名+ファイル名を指定します。

export DB2DSDRIVER_CFG_PATH=/work/node/db2dsdriver.cfg

余談:マニュアルに「DB2DSDRIVER_CFG_PATH レジストリー変数」と書かれているのでレジストリー変数なのかと思ったら、環境変数でした。db2setコマンドでは設定できません。このことにびっくり。

db2setで設定できるレジストリー変数なのかは、db2set -lr コマンドで確認。

db2set -lr | grep -i DB2DSDRIVER
(出力なし)

サンプルコード

  • T3表のスキーマを指定せずSELECT実行する。(SELECT .. FROM T3)
  • db2dsdriver.cfg (CurrentSchema = db2inst1) が有効であれば、db2inst1スキーマのT3表がSELECTされるはず。
  • db2dsdriver.cfg が読み込まれていない場合、user1スキーマのT3表をSELECTしようとして、そんな表はないというエラーとなる。
settings.js
exports.host='localhost';exports.port=50000;exports.dbname='TESTDB';exports.username='user1';exports.password='xxxxxxxx';
test2.js
varibm_db=require('ibm_db');varsettings=require('./settings');vardb_con_str="DRIVER={DB2}"+";DATABASE="+settings.dbname+";HOSTNAME="+settings.host+";UID="+settings.username+";PWD="+settings.password+";PORT="+settings.port+";PROTOCOL=TCPIP";varsql_str="select C1, C2 from T3 where C1=?";varparam1=[1];ibm_db.open(db_con_str,function(err,conn){if(err)returnconsole.log(err);conn.query(sql_str,param1,function(err,data){if(err)console.log(err);console.log(data);conn.close(function(){console.log('done');});});});

実行結果

db2dsdriver.cfgに設定したとおり CurrentSchema が db2inst1 に設定され、
"select .. from T3" と書いたSQLが、"select ... from db2inst1.T3" として実行されました。

$ node test2.js
[
  { C1: 1, C2: 'a       ' },
  { C1: 2, C2: 'b       ' },
  { C1: 10001, C2: 'A10001  ' }
]
done

Firestore.Timestamp に触れたくないあなたのための黒魔術

$
0
0

Firestore.Timestamp って、いらなくね?

ちゃんと使いこなせばメリットもあるんだろうなぁと思いつつ・・・
少なくとも小規模なプロジェクトでは、Timestamp型が存在するせいで

  • Firestore から読んだ、時刻フィールドを toDate() で Date型にもどす
  • Document の interface と、 REST をまたぐ interface を分ける

みたいな対応が必要になるわけです。いちいちめんどいんすこれ。
Date 型で十分な状況もあるんです。

問題の例

interfaceUser{name:string;updatedAt:Date;}constwrite:User={name:"taro",updatedAt:Date.now(),};awaitfirestore.collection("users").doc(write.name).set(write);constread:User=awaitfirestore.collection("users").doc(write.name).get().then(dss=>dss.data());console.log(read);// updatedAt が Timestamp クラスになってるよ!

魔術の源: Timestamp を Date へ再帰的に変換するヘルパーメソッド

functiontimestampToDateRecursively(value:any):any{if(value==null){returnvalue;}elseif(value.constructor===Firestore.Timestamp){returnvalue.toDate();}elseif(Array.isArray(value)){returnvalue.map(timestampToDateRecursively);}elseif(value.constructor===Object){constconverted:any={};for(constkeyinvalue){converted[key]=timestampToDateRecursively(value[key]);}returnconverted;}else{returnvalue;}}

白魔術: いちいちヘルパーメソッドをかませる

  • どこで何が起きてるかわすりやすい。安全。
  • いちいち忘れたりする。めんどい。
constread:User=awaitfirestore.collection("users").doc(write.name).get().then(dss=>timestampToDateRecursively(dss.data()));// 毎回書く必要あり console.log(read);// updatedAt がちゃんと Date になってるね

黒魔術: DocumentSnapshot.data() をオーバーライドする

  • 意識しなくても勝手に変換されてる。便利。
  • 知らないと、Firestoreの仕様の誤解や混乱のもとになる。危険。
/**
 * DocumentSnapShot.data() で返すすべてのTimestamp型をあらかじめDate型へ変換するよう
 * プロトタイプをオーバーライドします
 */functionwrapDocumentSnapshotData(){console.log(`Wrapping DocumentSnapshot.data()`);constorigin=Firestore.DocumentSnapshot.prototype.data;Firestore.DocumentSnapshot.prototype.data=function(){constdata=origin.bind(this)();constconverted=timestampToDateRecursively(data);// ここ!returnconverted;};}wrapDocumentSnapshotData();constread:User=awaitfirestore.collection("users").doc(write.name).get().then(dss=>dss.data());// いちいち意識しなくていいconsole.log(read);// updatedAt がちゃんと Date になってるね

Mac Nodebrewを用いてNode.jsをインストールした時にバージョン有効化時にエラーが出た話

$
0
0

目的

  • インストール後のバージョン有効化時にエラーが出た話を簡易的にまとめる

事前作業

エラー内容

  • 最新の安定版をインストール後に下記コマンドを実行したところエラーが発生した。

    $nodebrew use vX.X.X 
    
  • エラー

    vX.X.X is not installed
    

解決方法

$ nodebrew --versionの出力の中の「# install」に記載されている$ nodebrew install vX.X.Xを再度実行した。

再度$ nodebrew use vX.X.Xを実行したところ正常にバージョンを有効化することに成功した。

Node.js 14が本日リリース!V8のアップグレードにより「オプショナルチェイニング」「Null合体演算子」等が利用可能に

$
0
0

この投稿では、Node.jsバージョン14の新機能を紹介します。

Node.js 14

  • 本日、2020年4月22日にリリースされた:tada:
  • v14はLTS(long term support)という3年間の保守が約束されたバージョン。

Node.js 14の今後の予定

  • Current期間: 2020年4月22日〜2020年10月19日
    • 互換性が保たれる範囲で新機能が追加される。
    • 企業ユーザとしては、アップグレードに備えて、この6ヶ月間にv14でのテストを実施すると良い。
  • Active LTSの期間: 2020年10月20日〜2021年10月18日
    • リリースラインに適切な範囲での新機能の追加、バグ修正と保守。
    • 企業ユーザが、本番環境をアップグレードするのに最適な時期。
  • メンテナンス期間: 2021年10月19日〜2023年4月
    • 重要なバグ修正とセキュリティ更新。

ちなみに、過去のLTSの終了時期は下記の通り:

  • Node.js 12: 2022年4月
  • Node.js 10: 2021年4月

Node.js 14の新機能

  • 診断レポート機能がstableに
  • V8のアップグレード
  • 非同期ローカルストレージAPI
  • ストリームAPIを大きく変更
  • ES Moduleの警告削除
  • 非推奨APIの削除

診断レポート機能がstableに

  • 診断レポートがNode.js 14でstableになった。
    • Node.js 12でexperimentalとして導入されていたもの。
  • 診断レポートは、問題特定のために、スタックトレースやヒープの統計情報、プラットフォームの情報、リソースの仕様状況、クラッシュ、メモリリーク、パフォーマンスの低下などをレポートとして収集できる機能。
  • Diagnostic Report | Node.js v14.0.0 Documentation

V8のアップグレード

V8アップグレードにより使えるようになったJavaScriptの新機能

Optional Chaining - オプショナルチェイニング
  • ?.演算子を使うことで、ネストされたオブジェクトで、プロパティのnullチェックをいちいちせずに、プロパティを深くまでたどって行ける機能。
// beforeletnameLength;if(db&&db.user&&db.user.name)nameLength=db.user.name.length;// Optional ChainingconstnameLength=db?.user?.name?.length;
Nullish Coalescing - Null合体演算子
  • ??: 左辺がnullundefinedの場合に右の値を返し、それ以外の場合は左の値を返す演算子。
constfoo=null??'default string';console.log(foo);// expected output: "default string"constbaz=0??42;console.log(baz);// expected output: 0
Intl.DisplayNames
  • 言語、地域、文字体系の表示名の一貫した翻訳を可能にするオブジェクト。
// 国/地域コードから国名/地域名を出力する例letregionNames=newIntl.DisplayNames(['en'],{type:'region'});console.log(regionNames.of('US'));//=> "United States"console.log(regionNames.of('JP'));//=> "Japan"regionNames=newIntl.DisplayNames(['ja'],{type:'region'});console.log(regionNames.of('US'));//=> "アメリカ合衆国"console.log(regionNames.of('JP'));//=> "日本"
Intl.DateTimeFormatcalendarオプションとnumberingSystemオプションの有効化
  • Intl.DateTimeFormatoptions引数にて、calendarnumberingSystemが使えるようになった。
consttoday=newDate('2019-05-10');console.log(newIntl.DateTimeFormat('ja',{calendar:'japanese'}).format(today));//=> R1/5/10console.log(newIntl.DateTimeFormat('ja',{calendar:'japanese',numberingSystem:'jpan'}).format(today));//=> R一/五/十console.log(newIntl.DateTimeFormat('ja',{calendar:'japanese',numberingSystem:'jpanfin'}).format(today));//=> R壱/伍/拾console.log(newIntl.DateTimeFormat('ja',{calendar:'japanese',numberingSystem:'jpanyear'}).format(today));//=> R元/5/10console.log(newIntl.DateTimeFormat('ja',{calendar:'japanese',numberingSystem:'jpan',era:'long'}).format(today));//=> 令和元年五月十日console.log(newIntl.DateTimeFormat('ja',{calendar:'japanese',numberingSystem:'jpanfin',era:'long'}).format(today));//=> 令和元年伍月拾日console.log(newIntl.DateTimeFormat('ja',{calendar:'japanese',numberingSystem:'jpanyear',era:'long'}).format(today));//=> 令和元年5月10日

非同期ローカルストレージAPIの実験導入

ストリームAPIを大きく変更

  • ストリームAPI全体の一貫性を改善した。
    • http.OutgoingMessagestream.Writableと一貫するように、
    • net.Socketstream.Duplexと一貫するように変更された。
  • 大きな変更だが、ほとんどのアプリケーションに影響はないと思われる。
    • エッジケースのみが変更されているため。
    • ただし、ストリームに大きく依存している場合は、Node.js 14のCurrent期間にしっかりテストしたほうがいい。

ES Moduleの警告削除

  • Node.jsでは、ECMAScript Modules(ESM)が使える。
    • ESM = require()module.exportsの代わりに、importexportを使うアレのこと。
  • Node.js 13では--experimental-modulesフラグなしにES Modulesが使えるようになった。
    • しかし、それでも「ExperimentalWarning: The ESM module loader is experimental.」という警告は出し続けていた。
  • Node.js 14では、上記の警告を出さないようにした。
  • ただし、ESMはNode.js14でも「実験的機能」扱い。
    • 実験的=将来的に互換性のない変更や削除があるかもしれない、ということ。
    • 本番環境でESMを使う場合は要注意。

参考文献

webpackのビルドにてエラー。 TypeError: Cannot read property 'properties' of undefined

$
0
0

エラー内容

TypeError: Cannot read property 'properties' of undefined

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @ build: `webpack`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the @ build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

今回のエラーで見るべきは、TypeError: Cannot read property 'properties' of undefinedの部分。

これは古いバージョンのwebpackを使っていることで吐かれるエラーみたいです。

解決法

僕の場合は

"webpack": "^4.5.0",
"webpack-cli": "^2.0.14"

から

"webpack": "^4.43.0",
"webpack-cli": "^3.1.1"

一度webpackのバージョンを確認してみてください!

Box APIでBox Shieldの分類の操作をしてみる

$
0
0

やりたいこと

カスタムアプリからAPIを通してBoxを利用する場合、Box Sheld(のスマートアクセス機能)って、APIから使えるの?どうやってセキュリティ分類をファイルにつけるの?を調査しました。

TL;DR

Box APIからもセキュリティ分類を操作して、Box Shield スマートアクセスを利用可能です。

やりかたはこんな感じです。

constboxSDK=require("box-node-sdk");constconfig=require("./config");// config.jsonconstmain=async()=>{constsdk=boxSDK.getPreconfiguredInstance(config);constsaClient=awaitsdk.getAppAuthClient("enterprise");// 社内ユーザー1として実行saClient.asUser("12510354596");// テストファイル2 = 656467312576awaitsaClient.files.addMetadata("656467312576",// ファイルIDsaClient.metadata.scopes.ENTERPRISE,"securityClassification-6VMVochwUWo",// 分類つけるときの定数{Box__Security__Classification__Key:"機密情報"}// この部分を変える);};main();

そもそもBox Shieldってどういうもの?

Box Shieldとは、Boxのアドオンで、Boxの標準のセキュリティを更に拡張するオプションです。
Box Shieldをざっと俯瞰で説明すると、大きくは2つの(全然ちがう)以下の機能があります。

  • スマートアクセス
    • 不注意、または悪意の操作による情報漏えいを予防
    • 分類されたフォルダ・ファイルに誤ってアクセス権を付与してもアクセス不可
  • 異常検知
    • ユーザーの操作をAIで分析、悪意のある操作を検出する
    • 不審な場所(警戒地域からのアクセス)、不審なセッション(特定IDに対して複数の地域からの同タイミングのアクセス)、異常なダウンロード(通常発生しないような大量ダウンロード)を検出

今回対象とするのは、スマートアクセスの方です。

スマートアクセス(アクセスポリシー)の使いかた

基本的なスマートアクセスの使いかたは以下の3ステップ。簡単です。

  1. 分類を作る
  2. アクセスポリシーを作る
  3. ファイルやフォルダに分類を適用する

参考: https://support.box.com/hc/en-us/articles/360044196353-Using-Smart-Access

画面でみていきましょう。

1. 分類を作る

まず分類を作ります。分類はメタデータの一種です。分類自体はただのメタデータなので、単体では何もできません。アクセスポリシーと一緒につかって初めて価値が出ます。分類はGovernanceでリテンションのためにもつかわれたりします。

Box 管理コンソール > 分類 > 新規作成ボタンを押し、以下の項目を入力し保存ボタンを押します。

screencapture-trial-entgov-app-box-com-master-classification-create-1587521524090.png

分類名:わかりやすい名前をつけます。ここでは「機密情報」にしてみます。

ラベルカラー:視認性を上げるために、ここでは赤を選んでみます。下にプレビューの例でどのように見えるかがわかります。

分類の定義:ここに書いた文章が、各コンテンツについた分類にマウスオーバーした時やコンテツの詳細ウィンドウに表示されます。わかりやすい文章を入れておきます。

2. アクセスポリシーを作る

次にアクセスポリシーを作ります。

Box 管理コンソール > Shield >アクセスポリシー > ポリシーの作成ボタンを押し、以下の項目を入力します。

(Box Shieldのアドオンを有効にしていないテナントではShieldが表示されません)

screencapture-trial-entgov-app-box-com-master-shield-access-policies-43281157-edit-1587527157106.png

ポリシー名:ここでは「機密情報ポリシー」としておきます。

説明:わかりやすいポリシーの説明を入れます。

コンテンツの種類:ここで、1.分類を作るで作った分類を選びます。

分類を選ぶ他に、分類がついていないコンテンツ全てに適用するアクセスポリシーとすることも可能です。

セキュリティコントロール:ここで実際にどのような制御をしたいのかを決めます。

セキュリティコントロールでは、以下の5種類のコントロールを組み合わせて設定可能です。

  • 1.外部コラボレーションの制限

外部コラボレーションに関する制限です。
Boxの画面上で共有を押した時の上半分の「ユーザーを招待」に関しての制限です。
以下から選択します。
(1)すべての外部コラボレーションをブロック、(2) 選択した組織以外の全員がコラボレーション可能、(3)しょうにんされた組織のみがコラボレーション可能
このサンプルでは、(1)すべての外部コラボレーションをブロック、を選択しておきます。

  • 2.共有リンクの制限

リンクの共有に関する制限です。
Boxの画面上で共有を押した時の下半分の「リンクを共有」に関しての制限です。
以下から選択します。
(1) 公開、(2)会社およびコラボレーターにのみ、(3)コラボレータのみ
この記事では、(3)コラボレータのみとしておきます。

この他、以下のような制限も可能となりますが、ここでは割愛します。

  • 3.ダウンロードと印刷の制限
  • 4.アプリケーションの制限
  • 5.FTPの制限

3. ファイルやフォルダに分類を適用する

フォルダ/ファイルに対して、分類として、上記で作成した機密情報の分類をセットするだけです。

確認のため以下のシナリオを用意しました。

テストのための状況

  • 「テストフォルダ」の所有者が社内ユーザー1
  • 「テストフォルダ」には、「テストファイル1」と「テストファイル2」が入っている。
  • 「テストフォルダ」に社内ユーザー2と社外ユーザーAが、編集者として招待されている
  • 「テストフォルダ」への共有リンクが、リンクを知っている全員+表示およびダウンロード可能になっている

わかりずづらいので関係を図にします。

テストの関係.png

テストファイル1、テストファイル2ともに、全員アクセス出来る状況です。
社内ユーザー1は所有者として、社内ユーザー2と社外ユーザーAはコラボレーターとして。
公開共有リンクを経由した場合、これ以外のユーザー例えば社内ユーザー3や社外ユーザーBもテストフォルダ配下にアクセス可能です。

分類を付けてみる

では、ここで先程作成した分類:機密情報を、テストファイル1につけてみます。

社内ユーザー1と社内ユーザー2にとって変化はありません。
社外ユーザーAからみると、テストファイル1とテストファイル2は以前のように一覧はできています。
ただし、テストファイル1の横にバージョン番号が表示されなくなりました。
公開共有リンクを経由して参照している社外ユーザーBも社外ユーザーAと同じ状態です。

スクリーンショット 2020-04-22 14.45.53.png

ここで、社外ユーザーAが、テストファイル1を開いてみると、以下のような画面になります。
これは公開共有リンク経由で開いている社外ユーザーBも同じ挙動になります。

スクリーンショット 2020-04-22 14.46.10.png

分類が表示されており、どういう理由でブロックされているのかが明確です。

以上が、基本的なBox Shieldのスマートアクセスの使いかたです。
社内ユーザーが誤った共有操作をしても、分類をつけておくことで(あるいはつけない場合のアクセスポリシーを設定することで)予防的・明示的にアクセス制限ができますね。

APIからの使いかた

さて、ここまで長々とBox Shieldについて書いてきましたが、これをAPIから操作できるのか?というのが今回のお題です。

分類を作るところからできそうですが、今回は、テストファイル1についている分類「機密情報」を取り出して、テストファイル2につけてみることを今回のゴールとしてやってみましょう。

とりあえずユーザーIDとファイルIDを用意

事前準備として、テストファイル1にアクセスできる社内ユーザー1のユーザーIDを取り出します。

※ 管理対象ユーザーの情報を抜き出すには、開発者コンソール > 当該アプリ > 構成 > アプリケーションアクセスを、"Enterprise"にセットするひつようがあります。合わせて、高度な機能で、ユーザーとして操作を実行もONにしておきます。

さらに、操作対象のファイルIDも用意します。
このあたりは苦労しない部分だとおもうのでコードは割愛します。
IDが以下の通りだとわかりました。

社内ユーザー1のid: "12510354596"
テストファイル1のid: "656474520915"
テストファイル2のid: "656467312576"

テストファイル1のメタデータを取り出してみる

constboxSDK=require("box-node-sdk");constconfig=require("./config");// config.jsonconstmain=async()=>{constsdk=boxSDK.getPreconfiguredInstance(config);constsaClient=awaitsdk.getAppAuthClient("enterprise");// 社内ユーザー1として実行 (Service Accountではテストファイル1が見れない)saClient.asUser("12510354596");// テストファイル1の全てのメタデータを取り出してみるconstmetadatas=awaitsaClient.files.getAllMetadata("656474520915");console.log(metadatas.entries);};main();

console.logの出力内容がこちらです。

[{'$type':'securityClassification-6VMVochwUWo-43a8a5c1-c0ed-4ee5-bab4-1cd373af5054','$parent':'file_656474520915','$id':'ecca948a-23a0-4bf9-9719-77d965320888','$version':1,'$typeVersion':4,Box__Security__Classification__Key:'機密情報','$template':'securityClassification-6VMVochwUWo','$scope':'enterprise_324763110','$canEdit':true}]

Box__Security__Classification__Keyの部分だけプロパティ名が$からはじまっていませんが、このKey:Valueだけが、通常のメタデータでいうところのデータのようです。

'$template': 'securityClassification-6VMVochwUWo'この長々しい名前が固定で分類のメタデータ・テンプレートの名前になるようです。

テストファイル2に分類を適用してみる

それでは、取り出した分類の情報をもとに、別ファイル(テストファイル2)に分類を適用してみます。
以下のコードで実現できました。

constboxSDK=require("box-node-sdk");constconfig=require("./config");// config.jsonconstmain=async()=>{constsdk=boxSDK.getPreconfiguredInstance(config);constsaClient=awaitsdk.getAppAuthClient("enterprise");// 社内ユーザー1として実行saClient.asUser("12510354596");// テストファイル2 = 656467312576awaitsaClient.files.addMetadata("656467312576",// ファイルIDsaClient.metadata.scopes.ENTERPRISE,"securityClassification-6VMVochwUWo",// 分類つけるときの定数{Box__Security__Classification__Key:"機密情報"}// この部分を変える);};main();

ポイントは、securityClassification-6VMVochwUWoという決められた固定のテンプレート名を渡すことと、
{ Box__Security__Classification__Key: "機密情報" }という可変部分のデータを渡すことです。

ちなみにaddMetadataを使うと重複して分類が付く場合エラーになりなるので注意してください。
このへんはカスケーディングポリシーとも関わる部分のようです。別途カスケーディングポリシーについても記事が書ければとおもっています。

promise-queue: 100件から常に3件取り出して並列実行

$
0
0

Node.jsにもasync / await構文が実装されたが、Queueみたいな仕組みがない。

for文で1件ずつawaitで回していると何時まで経っても終わらない。
かといって、一気にPromiseに変換して、Promise.allで見張る仕組みにすると
同時実行数制限で止まってしまうといった問題も出てくるだろう。

探したらpromise-queueという神ライブラリを見つけたので、
紹介しつつ活用していきたい。

Installation

promise-queueはnpmでインストールできる。

$ npm install promise-queue

Usage

Exampleの各セクションを元に加工

constQueue=require('promise-queue');constmaxConcurrent=3;// 並列実行するタスク数constmaxQueue=Infinity;// キューの最大数constqueue=newQueue(maxConcurrent,maxQueue);

これで3件までしか並列実行されないQueueが作成される。
実際のソースコードを読む限り引数はどちらも省略可能。
初期値はどちらもInfinifyとなっている。

後はPromiseを返す関数を1個ずつaddメソッドに渡してやれば良い。

// キューを宣言constQueue=require('promise-queue');constqueue=newQueue(3);// 100個のやりたいことを定義するconstsomething=Array(100).fill(0).map((_,i)=>i+1);console.log(something);// [1, 2, 3, ... 98, 99, 100]constmain=async()=>{constresolves=[];for(constitemofsomething){// 戻り値がPromiseの関数を渡すqueue.add(()=>db.insert(it));}}main();

Test

ではNode.jsのREPLで挙動を確認してみよう。

$ node
> const Queue = require('promise-queue');> const queue = new Queue(3);> const something = Array(100).fill(0).map((_, i)=> i + 1);> const resolves =[];

ここでresolvesという空配列を定義する。
更にnew Promise時に払い出されるresolve関数をresolves配列に格納していく。
REPL上の任意のタイミングでresolveを発火させながら状況を確認出来るようにしていく作戦だ。

> something.forEach(it =>
... queue.add(()=>
..... new Promise(resolve =>{
....... resolves.push(resolve);
....... })
..... )
... );

こんな感じ

queueaddで突っ込まれた100個の関数の内、
まだ3個しか実行していないはずなので、
resolvesに格納されたresolve関数も3個だけになるはずである。

> resolves.length
3
> queue.getQueueLength()
97
> queue.getPendingLength()
3

完璧。
そしてresolves配列に入っている関数を実行すれば、
更に次の関数が実行されてresolves配列内の関数がどんどん増えるはずである。

> resolves[0]()> resolves.length
4
> queue.getQueueLength()
96
> queue.getPendingLength()
3
> resolves.forEach(it => it())> resolves.length
7
> queue.getQueueLength()
93
> queue.getPendingLength()
3

npmで配布されているだけあって完璧な挙動だ。

もしこのライブラリが無かった場合、
Queueを実現する為にあちこちに無駄なthenを仕込む事になる。
可読性は最悪になっていただろう。

Async関数を突っ込むだけでキューになるpromise-queueは、
もうバッチ処理には欠かせない存在と言っても過言ではなさそうだ。

Example

全部終わったら処理を継続してほしかったので、
更に関数でラッピングしてAsync関数の配列を突っ込むとPromiseを返す関数を作ってみた。
(元のコードはLiveScript、JSに手動コンパイルしただけでテストしてないので、エラーが出たらごめんなさい)

src/functions/queue.js
constQueue=require('promise-queue');constisFinished=queue=>queue.getQueueLength()===0&&queue.getPendingLength()===0;module.exports=(count,asyncFns)=>{queue=newQueue(count);returnnewPromise(resolve=>asyncFns.forEach(asyncfn=>{awaitqueue.add(fn);if(isFinished(queue))resolve();}));}

これを使って100件のユーザをfor文回して登録するということをやってみたい

なお、この例は実務では先輩から「ばかやろう!バルクインサートでやれ!」と叱られてしまうので、
同時入力数が制限されているゴニョゴニョな処理は各自考えて欲しい。

constqueue=require('./functions/queue.js');constmysql=require('mysql2/promise');constconn=mysql.createConnection(opt);constusers=[{name:"taro",age:18},{name:"jiro",age:17},// こんな感じのデータが大量に存在する];constmain=asyncfunction(){// この二重関数定義がポイントconstasyncFns=users.map(user=>()=>conn.query("INSERT INTO users SET ?",user););awaitqueue(3,asyncFns);console.log("全件インサート完了!");}main();

Box APIで、外部ユーザーを取得するやりかた

$
0
0

Boxで外部のユーザーを取るやり方(box-node-sdkの場合)

box-node-sdkをつかって、外部ユーザーを取得するやりかたがわからなかったので調べました。
他のSDKでも考え方は同じはず。

以下やり方。

constboxSDK=require("box-node-sdk");constconfig=require("./config");// config.jsonconstmain=async()=>{constsdk=boxSDK.getPreconfiguredInstance(config);constsaClient=awaitsdk.getAppAuthClient("enterprise");constanExternalUser=awaitsaClient.enterprise.getUsers({filter_term:"taro.yamada@example.com",user_type:"external",});console.log(anExternalUser);};main();

ポイントは、filter_termnameもしくはloginを完全な形で入れることと、user_typeexternalを指定することみたいです。
外部ユーザーの取得は、1レコードずつしか取れないようですね。

1レコードずつしか取れないのはいいとして、そもそも、名前とかLoginをどうやって取得するんだって話もあります・・・。
コラボレーションとかから引っ張ってくる感じなのかなと想像しますが、今度調べときます。


direnvとnvmを用いてディレクトリごとにnodeのバージョンを切り替える

$
0
0

概要

  • 個人や仕事で、複数のプロジェクトに渡ってやっていくうちに、nodeのバージョンを切り替えるのが煩わしくなっくる。
  • 私自身もnvm use v12などと明示的に切り替えていた。
  • 今回はそう言った方のために、nodeのバージョンを自動的に切り替える手順を紹介します。

direnv と nvm について

Githubのdirenvnvmを参考

direnvのインストール & 初期セットアップ

direnvについてはこちらが良い記事だったので、はじめに入れておく。
https://qiita.com/kompiro/items/5fc46089247a56243a62

nvmのインストール & 初期セットアップ

nvmについてはこちらが良い記事だったので、はじめに入れておく。
Macの方はHombrewを使うと思うがGithubには公式でサポートしないと書いてあるので、認識だけしておく。(私も使っているが問題になったことはない)
https://qiita.com/ffggss/items/94f1c4c5d311db2ec71a#nvm%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

direnvとnvmを用いてディレクトリごとにnodeのバージョンを切り替える

direnvの設定にuser_nvm()を加える

~/.config/direnv/direnvrcもしくは ~/.direnvrcに書く

~/.direnvrc
use_nvm(){local node_version=$1nvm_sh=~/.nvm/nvm.sh
  if[[-e$nvm_sh]];then
    source$nvm_sh
    nvm use $node_versionfi}

(https://github.com/direnv/direnv/wiki/Node#using-nvmより引用)

.envrcに切り替えたいnodeのバージョンを書く

切り替えたいディレクトリに.envrcを作成。
細かいバージョン指定でなく、v14などとざっくりで書いておけば、nvmによって自動で選ばれるので、おすすめ

.envrc
use nvm v14

direnvが使えるように許可 & 読み込み

❯ direnv allow                                                                                                                                                                 
direnv: loading .envrc                                                                                                                                                                     
direnv: using nvm v14
Now using node v14.0.0 (npm v6.14.4)
direnv: export ~NVM_BIN ~PATH

うまく切り替わったことがわかる。次に、試しにディレクトリを切り替える

cd ..
direnv: loading ../../../../.envrc
direnv: using java 8.0.242.hs-adpt
direnv: export +CPATH +LD_LIBRARY_PATH +LIBRARY_PATH +PKG_CONFIG_PATH ~JAVA_HOME ~MANPATH ~PATH

 ❯ cd -
direnv: loading .envrc
direnv: using nvm v14
Now using node v14.0.0 (npm v6.14.4)
direnv: export ~NVM_BIN ~PATH

ディレクトリを切り替えてもうまく切り替わっていればOK

Node.jsによる簡易httpサーバ

$
0
0

0. メモ

2019年9月後期授業用に少し改変

1. Node.jsによる簡易httpサーバ

1.1 node.jsをインストール

ここからダウンロードする。
https://nodejs.org/en/download/
インストールは右クリックでインストール(とりあえず一般ユーザでやってみる)
node-v10.15.3-x64msiは、環境変数の設定は勝手にやってくれる(C:\Program Files\nodejs\;をパスに入れる等)ので、ありがたいが、勉強にはならないようにも思う。トラブル要因が一つ減るのでまぁいいが。

環境変数は、こんな形でできていると良い。
(他の環境変数の設定が間違っていたりすると、手動で直す必要がある事例あり)

image.png

起動チェックはコマンドプロンプトから以下のコマンドを使う。バージョン番号(10.15.3)が帰ってくれば成功。
node -v

1.2 簡単サーバの立上げ実験

例えばc:\jstest等のフォルダを作り、
以下のプログラムを打ち込んで、UTF-8で保存する。

testsrv.js
varhttp=require('http');// event handler HTTPhttp.createServer(function(req,res){res.writeHead(200,{'Content-Type':'text/plain'});res.end('Hello World!\n');}).listen(8181,'127.0.0.1');

スタートメニューにNode.js command promptというのができているので、それをクリック
image.png

コマンドプロンプトが立ち上がる。以下でプログラムで実行。
node testsrv.js
コマンドプロンプトには何も表示されない(それで大丈夫)
通信ポートを使うので最初にセキュリティの許可を求めてくる可能性がある。許可すること

ブラウザから以下にアクセスする
http://127.0.0.1:8181/
「Hello Word!」が表示されたら正常終了
終了は、コマンドプロンプトでCtrl+C。

1.3 コンテンツも返す簡易サーバ

以下のコマンドラインでプログラムで実行。
node httpServ.js

今度は、以下のように3通りのアクセスをしてみる。

httpServ.js
varhttp=require("http");varfs=require('fs');functiongetType(_url){vartypes={".html":"text/html",".png":"image/png",".gif":"image/gif"}for(varkeyintypes){if(_url.endsWith(key)){returntypes[key];}}return"text/plain";}varserver=http.createServer(function(req,res){varurl=req.url;url=url.replace(/^\//,"");console.log(url);if(fs.existsSync(url)){fs.readFile(url,(err,data)=>{if(!err){res.writeHead(200,{"Content-Type":getType(url)});res.end(data);}else{res.statusCode=500;res.end();}});}else{res.statusCode=404;res.end();}});server.listen(8181,'127.0.0.1');

2. サーバ側のプログラム実行

CGIによるプログラム実行をする簡易サーバのサンプル

cgitest.js
varhttp=require('http');varurl=require('url');varpath=require('path');varserver=http.createServer();server.on('request',doRequest);server.listen(8181);functiondoRequest(req,res){varurl=req.url;console.log(url);constexecSync=require('child_process').execSync;varresult=execSync("test2").toString();res.writeHead(200,{'Content-Type':'text/html'});res.write(result);res.end();}

C言語で作るtest2.exeのためのソースコード

test2.c
#include <stdio.h>
#include <stdlib.h>
intmain(void){printf("<!DOCTYPE html>\n");printf("<html lang = \"ja\">\n");printf("<head>\n");printf("This is CGI test\n");printf("My name is XXXXXXXX<BR>\n");printf("</html>\n");return0;}

※※実験科目はこの先も続く(授業科目はここまでで終わり)

3. フォームアプリを使ったシリアル通信の状態表示

目標:c#のフォームアプリでHTTPアクセス

画像を見ることになるので、C#のフォームアプリで作成する。
フォームアプリは(いや、教材以外の多くのアプリケーションは)、複数のソースから構成される。編集の際にどのファイルを扱っているのか気を付けることがとっても重要。でも、慣れないと中々難しいので、初めて挑戦する人は、失敗を繰り返す上での、練習の第一歩のつもりで。

3つのボタンで表示できるようにButton1_Click, Button2_Click, Button3_Clickの3つのボタンを作る。
とりあえずForm1というWindowに3つのボタンと、イメージ表示用のpictureBox1という、画像表示用のボックスを作ってみた。
こんな感じ
image.png

フォーム画面からソースを表示させるには、ソリューションエクスぷローラーでForm1.csを選択し、右クリック。そこからソースを表示を選ぶ。
image.png

ソースコードは、Com2HttpReqプロジェクトのFrom1.csの先頭部分だけ。
ソースコードの中にもButton1_Click, Button2_Click, Button3_Clickの3つのボタンクリックに対応した関数がある。
ボタンアクセスは、何も工夫なく愚直なやり方でwebアクセスをする。C#になると、Webアクセス用の構造は次のようになっており、非常に簡単。

Form1.cs
namespaceCom2HttpReq{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();}privatevoidStartSerial(objectsender,EventArgse){SerialPort1.PortName=comboBox1.SelectedItem.ToString();// 選択されたCOMをポート名に設定SerialPort1.Open();SerialPort1.Write("s");}privatevoidStopSerial(objectsender,EventArgse){SerialPort1.Close();}privatevoidButton1_Click(objectsender,EventArgse){WebClientwc=newWebClient();Streamstream=wc.OpenRead("http://127.0.0.1:8181/gu.png");Bitmapbitmap=newBitmap(stream);stream.Close();pictureBox1.Image=bitmap;textBox1.Text="gu";}privatevoidButton2_Click(objectsender,EventArgse){WebClientwc=newWebClient();Streamstream=wc.OpenRead("http://127.0.0.1:8181/choki.png");Bitmapbitmap=newBitmap(stream);stream.Close();pictureBox1.Image=bitmap;textBox1.Text="choki";}privatevoidButton3_Click(objectsender,EventArgse){WebClientwc=newWebClient();Streamstream=wc.OpenRead("http://127.0.0.1:8181/pa.png");Bitmapbitmap=newBitmap(stream);stream.Close();pictureBox1.Image=bitmap;textBox1.Text="pa";}

4. 加速度計測結果表示アプリケーション

1) シリアル通信の結果をHTTP送受信をした上で画面表示

目標:目標:IIからIIIまでの内容をまとめて、加速度センサの状態を検知して、画面表示(およびLED表示)するアプリケーションを作成する。

C#のフォームアプリとArduinoのシリアル通信のプログラムは以下の渥美先生のページが非常に参考になる。
https://www.atsumitakeshi.com/index.html

でも時間がないので、

とりあえず(こちらで)III-2にボタンを増したものを作っておきました。
image.png

ヘッダ部分を更新

usingSystem;usingSystem.Drawing;usingSystem.Net;usingSystem.Windows.Forms;usingSystem.IO;usingSystem.Threading;

通信開始/終了用の関数を用意する。

送信開始時はII-3)で行った文字を一文字だけ送るという処理をしている。

privatevoidStartSerial(objectsender,EventArgse){SerialPort1.PortName=comboBox1.SelectedItem.ToString();// 選択されたCOMをポート名に設定SerialPort1.Open();SerialPort1.Write("s");}privatevoidStopSerial(objectsender,EventArgse){SerialPort1.Close();}

データ受信部分を追加

ここに、マイコンのシリアル回線から受信された1行を、処理するルーチンを作る。
文字列解析はこだわり始めると大変なので、 if(data.StartWith("A"))という形で先頭文字だけで判断する仕様にする。

//受信用データ処理privatevoidSerialPort1_DataReceived(objectsender,System.IO.Ports.SerialDataReceivedEventArgse){try{//string data = SerialPort1.ReadExisting();stringdata=SerialPort1.ReadLine();if(!string.IsNullOrEmpty(data)){//受信データに関する処理if(data.StartsWith("A")){WebClientwc=newWebClient();Streamstream=wc.OpenRead("http://127.0.0.1:8181/gu.png");Bitmapbitmap=newBitmap(stream);stream.Close();//サブスレッドの受信データを、別スレッドのFrom1にInvoke((MethodInvoker)(()=>// 受信用スレッドから切り替えてデータを書き込む{pictureBox1.Image=bitmap;textBox1.Text=data;// ラベルを受信した文字列へ変更Thread.Sleep(1);//実行待ちのスレッドに処理を渡す}));}elseif(data.StartsWith("B")){//******* 注意、ここは略 *******}else{//サブスレッドの受信データを、別スレッドのFrom1にInvoke((MethodInvoker)(()=>// 受信用スレッドから切り替えてデータを書き込む{textBox1.Text=data;// ラベルを受信した文字列へ変更Thread.Sleep(1);//実行待ちのスレッドに処理を渡す}));}}}catch(Exceptionex){MessageBox.Show(ex.Message);}}

IIIの最後のプログラムを実行するためには、COMポートを選択してStartボタンを押す。止めるときはStopボタンを押す。
マイコン側の通信プログラムとしては、arduinoで次のようなプログラムを走らせる。

AnaAccel.c
voidsetup(){pinMode(8,OUTPUT);pinMode(12,OUTPUT);Serial.begin(9600);}voidloop(){inti,s;intx,y,z;//実行・リセット直後digitalWrite(8,HIGH);digitalWrite(12,LOW);while(Serial.available()==0);charc=Serial.read();// 一文字分データを取り出す。//スタートdigitalWrite(8,LOW);digitalWrite(12,LOW);s=0;while(1){x=analogRead(A0);y=analogRead(A1);z=analogRead(A2);if(s==0){if(x>600){s=1;Serial.println("A");digitalWrite(8,HIGH);}}else{if(x<550){s=0;Serial.println("B");digitalWrite(8,LOW);}}delay(100);}}

2) 加速度センサ計測の分析

目標:3つ以上の加速度センサの状態や動きを検知して、PCに伝えるマイコンモジュール(プログラム)を作成する。

ポイント1

Iで作成した静的な加速度計測モジュールを、LEDを光らせるだけではなくシリアル回線用のプロトコルを設計し、そのプロトコルを用いてIII-3で完成したC#プログラムで加速度計測の結果が表示できるようなシステムを開発せよ。
※C#プログラムとの連携で迷うよりも、LED制御やArduino IDEのシリアルツールを使い、仕様を確定させることが重要。

ポイント2

加速度の動的変化に対しても検知することにより、センサーに対する動きを検知することを試みよ(来週への課題)。
3週目は、より柔軟なサーバ駆動を実験する。今回はHTTPアクセスにフォームアプリを使ったが、来週の実験ではコンソールアプリでソフト開発をして構わない。

5.発展課題

Mrmr OSC controller を使い、スマホセンサの加速度計測値に応じてサーバに処理を投げる事が出来るように改変してみよう。

参考文献

C#によるフォームアプリの開発入門

Visual C#の入門に良いと思います。
1. プログラミングとゲームの杜 http://www.greenowl5.com/gprogram/index.html

色々メモ

Close メソッド
Stream クラスは Close メソッドも持っているが、Stream を閉じる操作(リソース破棄の一例)も Dispose メソッドを使ってやるべき。

Raspberry Piでnode.js簡単サーバ構築
https://note.mu/agw/n/n3d41dc352b14

https://qiita.com/MKLemma/items/aef171c9c6ff5dc6fbe1

さて、C#でゲームを作るのは良いとして、フレームワーク(ライブラリ?)はどうしましょうか。
今ならUnity一択ですかね。
XNAは開発止まったらしいですね。
MonoGameは昔触った時はいい感触だったんですけど、全然流行ってないですね。
DXLibもいいですよね。私が最初にゲーム作ったときに使ったのがDXライブラリなんですよ。

[AWS]基礎知識編

$
0
0

背景

AWSでよく出てくる用語やコマンドをまとめてみました。

基礎知識

用語

AMI = OSの情報やディレクトリ、ファイル、ブロックデバイスマッピングなどインスタンス作成に必要な情報を一通りまとめたもの(インスタンスはAMIのコピー)

キーペア = 公開鍵(パブリックキー)暗号における公開鍵(パブリックキー)と秘密鍵(プライベートキー)のペア

Elastic IP = サーバーを再起動しても同じIPアドレスを割り当てることができる性質を持つIP

ポート = 1つのサーバと複数のサーバをつなぐもの

セキュリティグループ = Amazon EC2インスタンスに適用可能なAWS標準のファイアウォール機能。EC2インスタンスへのアクセスを"許可"したり、トラフィックを"制御"することができるもの。

SSH = 暗号や認証の技術を利用して、安全にリモートコンピュータと通信するためのプロトコルのこと。離れた場所にあるサーバの中で作業を行いたい場合に利用する。

yumコマンド = Linuxにおけるソフトウェア管理の仕組み。MacOSにとってのhomebrewと同じ役割を果たします。yumの管理下にあるプログラムのバージョンを管理したり、一括でアップデートしたりできます。

パッケージ = LinuxOS下における、ある役割/機能をもったプログラムの集合

Node.js = サーバーサイドで動くJavaScriptのパッケージ。今後のデプロイに向けた作業の中で、CSSや画像を圧縮する際に活用される。

rbenv = Rubyのバージョンを簡単に切り替えてくれるツール。

ruby-build = rubyをインストールするためによく使われているrbenvのプラグイン。(rbenvとセット)

.bash_profile = ログインシェルがbashの状態でログインしたときに読み込まれる設定ファイル

rbenv rehash = インストールしたRubyを使用可能な状態にする(shimsへの反映)

Unicorn = 全世界に公開されるサーバ上で良く利用されるアプリケーションサーバ。

プロセス = PC(サーバ)上で動く全てのプログラムの実行時の単位。

worker = 分裂したプロセス全て

worker_processes = リクエストを受け付けレスポンスを生成するworker(ワーカー)の数。

working_directory = UnicornがRailsのコードを動かす際、ルーティングなど実際に参照するファイルを探すディレクトリを指定。

pid = 起動する際にプロセスidが書かれたファイルを生成します。その場所を指定する。

listen = どのポート番号のリクエストを受け付けることにするかを決めるもの

sudo = コンピュータさんに対するコマンドのひとつ。スーパーユーザ(や他のユーザ)の権限でコマンドを実行するときに使うコマンド。

Swap領域 = メモリが使い切られそうになった時にメモリの容量を一時的に増やすために準備されるファイル。

環境変数 = Railsからは ENV['<環境変数名>'] という記述でその値を利用することができる。コンピュータが持っている、値を入れておく箱(変数)。

secret_key_base = Cookieの暗号化に用いられる文字列です。Railsアプリケーションを動作させる際は必ず用意する。

vimコマンド = 高機能エディタ。コマンドでターミナルで使えるようになる。

アセットファイル = 画像・CSS・JSファイルの総称

PID = それぞれのプロセスに割り当てられた管理用の番号のこと

Capfile = Capistrano関連のライブラリのうちどれを読み込むかを指定できる。

DSL = ある特定の処理における効率をあげるために特化した形の文法を擬似的に用意したプログラム。

releasesディレクトリ = capistranoを通じてデプロイされたアプリをひとまとめにするファイル。

currentディレクトリ = current内に入っているアプリの内容が、現在デプロイされている内容

sharedディレクトリ = バージョンが変わっても共通で参照されるディレクトリが格納されるディレクトリ

よく使うコマンド

$vim~/.bash_profile                          # 環境変数書き込み

$ source ~/.bash_profile# 環境変数適用$sudoservicemysqldstatus# 状態を確認する$sudoservicemysqldstart# 起動コマンド$mysql-uroot-p   # PW入力してMySQLへ接続する$psauxwww|grepunicorn# 状態を確認する $kill-9〇〇   # プロセス強制終了 $catlog/unicorn.stderr.log   # ユニコーンのログを見れる$sudoservicenginxrestart# nginx再起動$sudoless/var/log/nginx/error.log   # nginxのログを見れる$bundleexeccapproductiondeploy   # 自動デプロイ$lesslog/capistrano.log   # capistranoのログを見れる

【Nuxt.js】導入に必要なサーバ要件について

$
0
0

サーバで動かすには何が必要なの?

まずはローカル環境でいろいろ試すとは思いますが、
実際にプロダクトとしてサーバにローンチするにあたり、結局のところ
動作させるにはどんな環境が必要なのか?という軸でざっくり説明したいと思います。
なので、Nuxt.jsそのものがどんなものなのか、はここでは割愛させていただきます。

そもそも何故こんな記事を書こうと思ったのか

自身はPHPを中心としたバックエンドよりの仕事が多かったため、
Vue.jsやNuxt.jsの台頭によるフロントエンドの隆盛を横目で流し見していました。
さすがにこりゃいかん、と、思い始めたのも2018年あたりという…
で、細々と勉強をしている中で、文法作法はさて置き、いまいち分からなくなるのが
開発とローンチ(サーバ)での動作環境についてでした。
詰まるところ、ローカルではNode.jsやらnpmやらwebpackやらVueCLIやら
あれもこれもインストールして大盛り上がりな状態で開発していくんですが
ローンチするサーバには結局、どんな要件があれば動くの?ってところが非常に分かりにくかったんですね。
その切り口でシンプルに解説している投稿もなく
(もしかしたら昨今のフロントエンドエンジニアは、肌感で当たり前になっていることなのかもしれないが…)
ここは自身に言い聞かせる意味でも、あえて記事にしておこうと思った次第です。

モード別に見るとこんな感じ

Nuxt.jsには、モードによりデプロイ結果が変わってくるという仕様があります。

Universal(ユニバーサル)Single Page App(シングルページアプリケーション)Generate(静的ファイル生成)
ssr(サーバーサイドレタリング)SPAってやつ      生のhtmlを書き出し
UIレンダリング全体を担うミドルウェアとして組み込む, プログラムの一部として使うvueファイルからhtmlに生成してしまう
静的ホスティング不可静的ホスティング可(buildしたdist一式アップすれば動く)     静的ホスティング可(buildしたdist一式アップすれば動く)
サーバにNode.jsいるサーバにNode.js不要     サーバにNode.js不要
phpと同じように、サーバ側でレンダリングしてhtmlを作成してからレスポンス返すフロントにてjsでhtmlをレンダリングするので、初回が遅い。あと、jsでhtml生成なので、SEO的に弱いただのhtmlサイト。ある意味では最強の状態
SEO OKSEO△ 個別のメタやOG×SEO OK
いろいろコスト高いコスト低い コスト低い
サクサク初回重い普通
中~大規模サイトLP,小~中規模ALL

※デフォルトはユニバーサルモード

なんというか、つまり
サーバ側でNode.jsさんがごにょごにょしてページを作ってからフロントに返し、フロントはそれをただ表示するだけ、か、
とりあえず最初に材料まるっとフロントに渡して、フロントのjsさんが頑張ってごにょごにょしてページをレンダリングして表示させるか、
はたまた、なんてことない生のhtmlのただの静的なサイトにするか、
という三つのやり口があるんです。

余談ですが、静的htmlのジェネレートもできるんだ、ってちょっと驚きでした。
これ、MT(MovableType)みたいな使い方もできるんだ…って。

あと、SPAのところで一つ補足
ダイナミックレンダリングって技術があるようで、
ボットのクロール用にはhtmlを読ませて、ブラウザからの人のアクセス時にはSPAのjsからの描画を見せるみたいな
使い分けを行うこともできるらしい。
しかし、SSR移行までの一時しのぎ、的な位置づけのようで、
定着させる必須技術というわけではないだ。

こう考えると、はっきりしてくるんですが、
ユニバーサル、つまりSSRするときは、当たり前ですがサーバ要件にnode必須ということですね。
で、SPAとなるとサーバ側でレンダリグする必要はないので、サーバにnodeは不要、ローカルの開発環境にだけあればよいですし、
Generateでも同じことですね。

結局のところ

Node.jsがいるんです。ただそれは、ローカルの開発環境には必須ということであり
SSRモードで利用しないのであれば、サーバにNode.jsは不要です。
という、ものすごく単純で当たり前のことに行き着くのでした。

Nuxt.jsをプロジェクトに導入する場合は、
必ずサーバ要件でNode.jsが使えるかどうか確認してからにしましょう。
SSRでいくぜ!なんて息巻いてみても、後でやっぱりできませんでした…なんてことがないようにね!

Node.js: 子プロセスの例外を親プロセスに送る方法

$
0
0

Node.jsのchild_processモジュールのforkで起動した子プロセスにて、例外が発生したとき、その例外を親プロセスに送る方法です。

const{fork}=require('child_process')if(process.send){// 子プロセスの処理process.on('uncaughtException',error=>{process.send(error)// 例外はメッセージとして送るprocess.exit(1)})thrownewError('Something wrong')}else{// 親プロセスの処理constchildProcess=fork(__filename,[],{serialization:'advanced'// このオプションが必須})// 例外はメッセージとして受け取るchildProcess.on('message',message=>{if(messageinstanceofError){console.log('Child process error:')console.error(message)}})}
Viewing all 8814 articles
Browse latest View live