20200407のNode.jsに関する記事は9件です。

【Node.js】複数のファイルパスをオブジェクトでの階層表現に変換する

経緯

Node.jsのfsを使って特定のディレクトリの中身をファイルパスにて取得した際に、ディレクトリの階層と互換性のあるオブジェクトに変換したかった。

結論

index.js
const data = [
  '/public/aaa/1.file',
  '/public/aaa/2.file',
  '/public/bbb/1.file',
  '/public/ccc/1.file',
  '/public/ccc/2.file',
  '/public/ccc/3.file',
  '/public/ddd/1.file'
];

const output = {};
let current;

for (const path of data) {
    current = output;
    for (const segment of path.split('/')) {
        if (segment !== '') {
            if (!(segment in current)) {
                current[segment] = {};
            }
            current = current[segment];
        }
    }
}

console.log(output);

/* 実行結果
{ public:
   { 
     aaa: { '1.file': {}, '2.file': {} },
     bbb: { '1.file': {} },
     ccc: { '1.file': {}, '2.file': {}, '3.file': {} },
     ddd: { '1.file': {} } 
   } 
}
*/

あとは取得した結果から

子要素に値が入っていたらディレクトリ
子要素に値が入っていなかったらファイル

と認識して分岐させれば色々使えると思う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dotenvで環境変数ではなく .env 使う | Node.js

$ npm install dotenv --save

プロジェクトのルートディレクトリに .env ファイルを作成します。

# これはコメント行
KEY1=VALUE1
KEY2=VALUE2

実装

require('dotenv').config();

if (typeof process.env.KEY1 == 'undefined') {
  console.error('Error: "KEY1" is not set.');
  process.exit(1);
}

console.log(process.env.KEY1);  //=> VALUE1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js: child_process.fork()で起動したプロセスを子子孫孫殺す方法

本稿では、Node.jsにて、子プロセス、そこから派生した孫プロセス、さらにそこから派生したひ孫プロセス……を、一括して終了する方法を説明します。

※説明にあたって、実行環境はUNIX/Linuxを前提にしています。

子プロセスを殺しても、孫プロセスは死なない

Node.jsのchild_process.fork()は、子プロセスを起動できて便利です。子プロセスの中で、fork()を使って、孫プロセスを起動することもでき、さらに、孫プロセスでfork()して、ひ孫プロセスを、といった具合に子プロセスはネストして起動することができます。

起動した子プロセスはsubprocess.kill()で終了することができます。しかし、これは直接の子プロセスしか殺すことができません。どういうことかというと、

  1. oya.js が ko.js のプロセスを起動する。
  2. ko.js が mago.js のプロセスを起動する。
  3. このとき、 oya.js が ko.js のプロセスをkill()したとする。
  4. ko.js は終了する。
  5. mago.js は生存する。 (※このとき、 mago.js はinitプロセスの養子に出され、親pidは1になる)

といった事態が発生します。

孫プロセスが残存するサンプルコード

上のようなシナリオを再現できるコードを書いてみたいと思います。

まず、oya.jsの実装:

oya.js
console.log('oya.js: running')

// SIGINTを受け付けたとき
process.on('SIGINT', () => {
  console.log('oya.js: SIGINT')
  process.exit()
})

// プロセスが終了するとき
process.on('exit', () => {
  console.log('oya.js: exit')
})

// 子プロセスを起動
const ko = require('child_process')
  .fork(__dirname + '/ko.js')

// 3秒後にproc2.jsを終了する
setTimeout(() => {
  console.log('oya.js: ko.jsを終了させてます...')
  ko.kill('SIGINT')
}, 3000)

// ko.jsが終了したとき
ko.on('exit', () => {
  console.log('> Ctrl-Cを押してください...')
})

// このプロセスがずっと起動し続けるためのおまじない
setInterval(() => null, 10000)

oya.jsはko.jsを起動し、3秒後にko.jsを終了するコードになっています。ko.jsをkill()する際には、SIGINTシグナルを送るようにしています。Linuxのシグナルについては、ここでは詳しく説明しません。ここでは単にSIGINTシグナルはプロセス終了を指示するものと考えてください。

次に、ko.js:

ko.js
console.log('ko.js: running')

// SIGINTを受け付けたとき
process.on('SIGINT', () => {
  console.log('ko.js: SIGINT')
  process.exit()
})

// プロセスが終了するとき
process.on('exit', () => {
  console.log('ko.js: exit')
})

// 孫プロセスを起動する
require('child_process')
  .fork(__dirname + '/mago.js')

// このプロセスがずっと起動し続けるためのおまじない
setInterval(() => null, 10000)

最後に、mago.js:

mago.js
console.log('mago.js: running')

// SIGINTを受け付けたとき
process.on('SIGINT', () => {
  console.log('mago.js: SIGINT')
  process.exit()
})

// プロセスが終了するとき
process.on('exit', () => {
  console.log('mago.js: exit')
})

// このプロセスがずっと起動し続けるためのおまじない
setInterval(() => null, 10000)

このコードを実行してみます:

$ node oya.js
oya.js: running
ko.js: running
mago.js: running
oya.js: ko.jsを終了させてます...
ko.js: SIGINT
ko.js: exit
> Ctrl-Cを押してください...

3秒後にこのような出力がされ、oya.jsがko.jsをkill()し、ko.jsが終了したことが確認できます。

一方、mago.jsはまだSIGINTを受け取っていませんし、終了もしておらず、残存しています。

ここで、Ctrl-Cを押すと、oya.jsとmago.jsにSIGINTが送信されます:

...
> Ctrl-Cを押してください...
^Coya.js: SIGINT
mago.js: SIGINT
mago.js: exit
oya.js: exit

このタイミングではじめて、mago.jsが終了することが分かります。

感想を言うと、ko.jsにSIGINTを送信したら、mago.jsにもSIGINTが伝搬されていくものと誤解していたので、この結果は意外でした。

起動したプロセスを子子孫孫殺す方法

では、起動した子プロセスをkill()したタイミングで、孫プロセスも終了になるようにするにはどうしたらいいのでしょうか? それについて、ここで説明したいと思います。

プロセスグループ = 「世帯」

まず、Linuxのプロセスの基本として、プロセスグループというものがあります。これはプロセスの「世帯」のような概念で、親プロセス、子プロセス、孫プロセスをグループ化するものです。たとえば、Bashでnodeプロセスであるoya.jsを起動すると、そこからfork()したko.jsやmago.jsは、同じプロセスグループに属し、同一のグループIDが与えられます。

psコマンドでグループID(GPID)を確認すると、現に同じグループIDが3つのnodeプロセスに割り当てられていることが分かります:

$ ps -xo pid,ppid,pgid,command | grep node | grep .js
PID   PPID  GPID  COMMAND
17553  3528 17553 node oya.js
17554 17553 17553 node ko.js
17555 17554 17553 node mago.js

この結果をよく見ると分かりますが、GPIDはoya.jsのプロセスID(PID)と同じです。つまり、親のPIDが子孫のGPIDになるわけです。

プロセスを「世帯」ごと殺す方法

Node.jsでは、グループIDを指定して、プロセスを終了させることができます。やりかたは、process.kill()にGPIDを渡すだけです。このとき、与える値は負の数にしてあげます。正の数を渡してしまうと、プロセスグループではなく個別のプロセスをkill()するだけになるので注意です。

const groupId = 123456
process.kill(-groupId, 'SIGINT')

ちなみに、シェルでCtrl-Cを押したときに、親・子・孫がもろとも終了されるのは、Ctrl-Cが送るSIGINTが親プロセスに対してではなく、プロセスグループに対して送られているからです。(要出典)

detached = 別世帯を作る

今回やりたいことは、oya.jsのプロセスは生かしつつ、ko.jsとmago.jsをkill()したいことです。しかし、GPIDを指定したkill()では、oya.jsまで終了してしまいます。三者とも同じGPIDだからです:

PID   PPID  GPID  COMMAND
17553  3528 17553 node oya.js
17554 17553 17553 node ko.js
17555 17554 17553 node mago.js

ko.jsとmago.jsを別のGPIDを割り振る必要があります。それをするには、fork()のオプションにdetachedを指定します。

oya.js
// 子プロセスを起動
const ko = require('child_process')
  .fork(__dirname + '/ko.js', [], {detached: true})

これを指定すると、ko.jsとmago.jsがいわば「別世帯」になり、別のプロセスグループに属するようになります。GPIDもoya.jsとは別のものが割り当てられているのが確認できます:

$ ps -xo pid,ppid,pgid,command | grep node | grep .js
PID   PPID  GPID  COMMAND
21404  3528 21404 node oya.js
21405 21404 21405 node ko.js
21406 21405 21405 node mago.js

プロセスを子子孫孫殺すoya.jsの完成形

以上を踏まえて、oya.jsを子プロセス、孫プロセスを一括して終了できるように変更すると次のようになります:

oya.js
console.log('oya.js: running')

// SIGINTを受け付けたとき
process.on('SIGINT', () => {
  console.log('oya.js: SIGINT')
  process.exit()
})

// プロセスが終了するとき
process.on('exit', () => {
  console.log('oya.js: exit')
})

// 子プロセスを起動
const ko = require('child_process')
  .fork(__dirname + '/ko.js', [], {detached: true}) // 重要な変更箇所!

// 3秒後にko.jsを終了する
setTimeout(() => {
  console.log('oya.js: ko.jsを終了させてます...')
  process.kill(-ko.pid, 'SIGINT') // 重要な変更箇所!
}, 30000)

// ko.jsが終了したとき
ko.on('exit', () => {
  console.log('> Ctrl-Cを押してください...')
})

// このプロセスがずっと起動し続けるためのおまじない
setInterval(() => null, 10000)

最後に、このoya.jsを実行して、ko.jsとmago.jsが一緒に終了しているか確認してみましょう:

$ node oya.js
oya.js: running
ko.js: running
mago.js: running
oya.js: ko.jsを終了させてます...
mago.js: SIGINT
ko.js: SIGINT
mago.js: exit
ko.js: exit
> Ctrl-Cを押してください...
^Coya.js: SIGINT
oya.js: exit

期待通り、ko.jsとmago.jsは同じタイミングでSIGINTを受け取り終了しています。oya.jsはCtrl-Cを押すまで生存していることも分かります。

以上、Node.jsのchild_process.fork()で起動したプロセスを子子孫孫殺す方法についての説明でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ReactNative+Typescript】ローカルDBでRealmを使う

概要

ReactNativeTypescriptのプロジェクトにローカルDBとしてRealmをインストール・使用する際のメモ。
環境は以下の通り。

  • node @10.15.0
  • react-native @0.61.5
  • typescript @3.5.3

インストール

nodeのバージョンが10.X系にする必要がある。

install --save realm

react-nativeのバージョンが0.59以下の場合は要link

iOSの場合

CocoaPods

cd ios
pod install

実装

import Realm from 'realm'

// スキーマ名
const HOGE_SCHEMA_NAME : string = 'HOGE';

// スキーマ定義
const HOGE_SCHEMA : Realm.ObjectSchema = {
    // スキーマ名
    name : HOGE_SCHEMA_NAME ,
    // 主キー(省略可)
    primaryKey : 'prop1',
    // プロパティ
    properties: {
        // 型のみ指定
        prop1 : 'int',
        // 型と初期値を指定
        prop2 : { type : 'string', default : 'hoge' },
    }
}

// データIF
interface Hoge {
    prop1 : number,
    prop2 : string,
}

// DB操作開始
Realm.open({
    schema : [HOGE_SCHEMA]
}).then((realm : Realm) => {
    // write()でトランザクションを開始する
    realm.write(() => {
        // primaryKeyを指定しているならcreate()はupsertとして働かせることもできる(その場合は第3引数にtrue)を渡す
        realm.create(HOGE_SCHEMA_NAME , { prop1: 0, prop2 : 'test' });
        // 削除はdelete()もしくはdeleteAll()
    });

    // objects(schemaName : string)でデータを取得
    const datas : Realm.Results<Hoge> = realm.objects(HOGE_SCHEMA_NAME);
});

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsを使用したAWS S3への画像アップロード

まずは、ここからs3へのアクセスキーと、シークレットきーを取得してください。

S3 Bucketを作る

$ npm i --save aws-sdk
create-bucket.js
const AWS = require('aws-sdk');

// Enter copied or downloaded access ID and secret key here
const ID = '';
const SECRET = '';

// The name of the bucket that you have created
const BUCKET_NAME = 'test-bucket'; //同じバケット名は作れない ユニーク必須

const s3 = new AWS.S3({
    accessKeyId: ID,
    secretAccessKey: SECRET
});

const params = {
    Bucket: BUCKET_NAME,
    CreateBucketConfiguration: {
        // アジアパシフィック (東京)
        LocationConstraint: "ap-northeast-1"
    }
};

s3.createBucket(params, function(err, data) {
    if (err) console.log(err, err.stack);
    else console.log('Bucket Created Successfully', data.Location);
});

こんな感じ。で、実行

$ node create-bucket.js

S3で画像をアップする

uplode-bucket.js
const fs = require('fs');
const AWS = require('aws-sdk');

// Enter copied or downloaded access ID and secret key here
const ID = '';
const SECRET = '';

const s3 = new AWS.S3({
    accessKeyId: ID,
    secretAccessKey: SECRET
});

const uploadFile = (fileName) => {
    // Read content from the file
    const fileContent = fs.readFileSync(fileName);

    // Setting up S3 upload parameters
    const params = {
        Bucket: "test-bucket",
        Key: 'uniquename.jpg', // s3へ保存される名前 ユニーク必須
        Body: fileContent
    };

    // Uploading files to the bucket
    s3.upload(params, function(err, data) {
        if (err) {
            throw err;
        }
        console.log(`File uploaded successfully. ${data.Location}`);
    });
};

uploadFile('/Users/user-name/Desktop/test.png'); //ここにファイルの実体を入れる

こんな感じ。で、実行

$ node uplode-bucket.js

参考

https://stackabuse.com/uploading-files-to-aws-s3-with-node-js/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『コピペOK』NodeでS3へ画像アップロード【2020】

まずは、ここからs3へのアクセスキーと、シークレットきーを取得してください。

S3 Bucketを作る

$ npm i --save aws-sdk
create-bucket.js
const AWS = require('aws-sdk');

// Enter copied or downloaded access ID and secret key here
const ID = '';
const SECRET = '';

// The name of the bucket that you have created
const BUCKET_NAME = 'test-bucket'; //同じバケット名は作れない ユニーク必須

const s3 = new AWS.S3({
    accessKeyId: ID,
    secretAccessKey: SECRET
});

const params = {
    Bucket: BUCKET_NAME,
    CreateBucketConfiguration: {
        // アジアパシフィック (東京)
        LocationConstraint: "ap-northeast-1"
    }
};

s3.createBucket(params, function(err, data) {
    if (err) console.log(err, err.stack);
    else console.log('Bucket Created Successfully', data.Location);
});

こんな感じ。で、実行

$ node create-bucket.js

S3で画像をアップする

uplode-bucket.js
const fs = require('fs');
const AWS = require('aws-sdk');

// Enter copied or downloaded access ID and secret key here
const ID = '';
const SECRET = '';

const s3 = new AWS.S3({
    accessKeyId: ID,
    secretAccessKey: SECRET
});

const uploadFile = (fileName) => {
    // Read content from the file
    const fileContent = fs.readFileSync(fileName);

    // Setting up S3 upload parameters
    const params = {
        Bucket: "test-bucket",
        Key: 'uniquename.jpg', // s3へ保存される名前 ユニーク必須
        Body: fileContent
    };

    // Uploading files to the bucket
    s3.upload(params, function(err, data) {
        if (err) {
            throw err;
        }
        console.log(`File uploaded successfully. ${data.Location}`);
    });
};

uploadFile('/Users/user-name/Desktop/test.png'); //ここにファイルの実体を入れる

こんな感じ。で、実行

$ node uplode-bucket.js

参考

https://stackabuse.com/uploading-files-to-aws-s3-with-node-js/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IoT Agency PlatformをHeroku上にデプロイする

Sigfoxが提供していたIoT Agency PlatformがGNU Affero General Public Licenseに基づくオープンソースとなりました。ここでは、IoT Agency PlatformをHeroku上にデプロイする方法を説明します。
image.png

IoT Agency Platformは、SigfoxやMQTT、Webhookに対応したIoTデバイスからデータを取得し、グラフや地図形式でダッシュボード化できるアプリケーションです。使い方に関しては、こちらを参考にしてください。

全体の流れ

Sigfox IoT Agency PlatformをHeroku上にデプロイする方法は下記の通りの流れとなります。
1. sigfox platformのGithubからHerokuにアプリケーションをデプロイする
2. MongoDBを立ち上げ、データベースを紐づける

sigfox platformのGitHubからHerokuにアプリケーションをデプロイする

デプロイ方法は2種類あります。下記ボタンをクリックして、UIウィザードに従いデプロイする方法と
image.png
Heroku CLIで下記のようにデプロイする方法です。

git clone https://github.com/IoT-Makers/sigfox-platform.git my-project
cd my-project
heroku apps:create my-project
git push heroku master

ここでは、前者の方法で説明します。

新しいアプリケーションを生成する

先程の"Deploy to Heroku"ボタンをクリックしApp nameとregionを選択します。App nameはユニークな名前になるように設定してください。
image.png
設定後、Deploy appボタンをクリックすると、デプロイが開始します。数分かかりますが、完成すると下記のような画面になります。
image.png
Manage Appボタンをクリックし、アプリケーションの管理画面に遷移します。

MongoDBを立ち上げ、データベースを紐づける

ここでは、MongoDBのアドオンをアプリケーションにアタッチします。mLabはMongoDBをDBaaS(Database-as-a-Service)として提供するものです。

mLabはmongoDB.Altasに統合されたため、mongoDB.Altasでも同じようなことができると思いますが、ここでは、mLabで進めます。

mLab MongoDBアドオンの追加

Heroku管理画面のResourcesタグ内にあるQuickly add add-ons from Elements検索フィールドで"MongoDB"をキーに検索するとmLab MongoDBというアドオンが表示されます。
image.png
mLab MongoDBを選択すると、下図の画面になりますので、有償版でも結構ですが、まずの動作確認としては、無償版(Sandbox - Free)を選択し、Provisionボタンをクリックしてください。
image.png

mLab MongoDBとの接続

追加された"mLab MongoDB"をクリックします。
image.png
mLabの管理画面が表示されます。
画面上に、MongoDB URIで接続する(To connect using a driver via the standard MongoDB URI)と書かれた下にmongodB://からはじまるURIが表示されています。このURIをHeroku側に設定してあげればOKです。
image.png
HerokuのSettingタグに遷移し、Config Vars(環境変数設定)をします。
image.png
新たに、MONGODB_URIをキーとし、先程のURI(下記)を貼り付けます。ただし、dbuserとdbpassword、dbnameのところは、データベースの接続アカウントに置き換える必要があります。(MONGOLAB_CYAN_URIの値をコピー&ペーストしても良いかも)
mongodb://<dbuser>:<dbpassword>@ds253348.mlab.com:53348/<dbname>

アプリケーションの再起動

Herokuの"More"のところにある**Restart all dynos"を選択し、アプリケーションを再起動します。
image.png
再起動は1,2分内で終わると思いますので、再起動後、"Open app"をクリックし、アプリケーションを表示します。
下図のようなログイン及びサインアップ画面が表示されれば成功です。
image.png

なお、ソースコードは、[リポジトリ名]\frontend\srcの中にあります。

補足

ちなみに、下記のフレームワークを使っているようです。

  • Backend: Loopback 3+
  • Frontend: Angular 6+
  • Real-time: Primus
  • Database: MongoDB
  • Pub-sub & queuing: RabbitMQ

なお、本投稿に当たっては、Antoine de ChasseyLouis Moreauそして、Guillaume Noelに感謝します。

Sigfox Japan KCCS
Twitter @ghibi

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

npmパッケージのvulnerability対応フロー

概要

  • npmプロジェクトで利用しているnpmパッケージ(依存パッケージ)でvulnerability(脆弱性)が見つかったときの対処フローについて記載します。
    image.png

(GitHub等が親切に"We found potential security vulnerabilities in your dependencies."のように通知してくれるので便利)

対応フロー

ざっくり全体像は以下のとおり。

image.png

①最新のコードを取得する

いうまでもないが、手元のコードは最新のコードにしておく

git pull

最新であることが確認された

Already up to date.

ちなみに、今回は自作プロジェクト(https://github.com/riversun/simple-date-format) で実際にハンズオンした

②プロジェクトが使用しているnpmパッケージが最新かどうか確認する

脆弱性対応の前に、いま自分のプロジェクトが使っているnpmパッケージを最新のものにする。

プロジェクトが使っているnpmパッケージが最新かどうかはnpm outdatedコマンドで確認することができ、最新バージョンがある場合は、その情報を表示してくれる

npm outdated

するとこのように、最新版が存在するパッケージを一覧表示してくれる
いくつかのパッケージで最新版があるようだ

image.png

パッケージをアップデートする

package.jsonに記載されるnpmパッケージ一覧は以下のようになっている。
(「^」がついたキャレット表記についてはこちらで説明)

package.json抜粋
  "@babel/core": "^7.8.4",
  "@babel/preset-env": "^7.8.4",
  "babel-jest": "^25.1.0",
   ...

パッケージのバージョンの付け方セマンティックバージョニングに従っている。

セマンティックバージョニングにおいて、メジャーバージョン、マイナーバージョンは以下の意味をもっている。
image.png

これをふまえ、「パッケージを最新にする」には2種類の方法があるといえる。(パッチバージョンを上げるというのもいれれば3種類だが本筋にあまり影響ないので割愛)

  • ②-1 マイナーバージョンまで最新にする
  • ②-2 メジャーバージョンまで最新にする

②-1をとるか ②-2をとるかはポリシー次第だがメジャーバージョンを最新にする場合はAPIの後方互換が無いことを想定しておいたほうがいい。

②-1 マイナーバージョンまで最新にする場合

キャレット表記「^」つきで定義されたパッケージのバージョンを、マイナーバージョンまでを最新にするにはnpm updateコマンドで可能

npm update

以下のように、マイナーバージョンまでが最新になった
(ただし、メジャーバージョンは最新になっていない。)

+ jest@25.2.7
+ babel-loader@8.1.0
+ cross-env@7.0.2
+ @babel/core@7.9.0
+ babel-jest@25.2.6
+ @babel/preset-env@7.9.0
+ webpack@4.42.1
added 36 packages from 17 contributors, removed 6 packages, updated 140 packages, moved 1 package and audited 263490 packages in 18.399s

②-2 メジャーバージョンまで最新にする場合

npm updateでは、マイナーバージョンまでしかアップデートしてくれなかったが、ここではメジャーバージョンまで容赦なくアップデートしてくれるパッケージnpm-check-updatesを導入する

以下のようにしてnpm-check-updatesをインストールする

npm install -g npm-check-updates

npm-check-updatesをインストールするとncuというコマンドが使えるようになる。

ちなみに、ncuコマンドだけをたたくと現在のパッケージバージョンと最新のパッケージバージョンを表示してくれるnpm outdatedコマンドのような動作をする

ncu

を実行すると、以下のように現在のバージョンと最新バージョンが表示される。

image.png

つづいて、ncu -uを実行すれば、上でみたとおりパッケージが最新バージョンになるようにpackage.jsonを書き換えてくれる

ncu -u

image.png

npm updateと違ってパッケージのインストールそのものはやってくれない。package.jsonが書き換わるだけなので、自分で npm install する必要がある

npm install

これでパッケージは最新になった

③npm auditで脆弱性のある依存パッケージを確認する

今、最新のパッケージにした状態だが、この状態でも脆弱性(valnerability)のあるパッケージが含まれていることがある。
npm auditコマンドを使えば脆弱性のあるパッケージを洗い出すことができる。

npm audit

をやってみたら、180個の脆弱性がみつかった。レベルはlow

found 180 low severity vulnerabilities in 263397 scanned packages
  run `npm audit fix` to fix 174 of them.
  6 vulnerabilities require manual review. See the full report for details.

④npm audit fixで自動修復をこころみる

npm audit fixをすると、脆弱性のあるパッケージのバージョンを自動的に脆弱性の無いバージョンに置き換えてくれる(努力をしてくれる)

npm audit fix

すると以下のようになった。
180個の脆弱性のうち174個が修正された。

removed 1 package and updated 2 packages in 4.641s

31 packages are looking for funding
  run `npm fund` for details

fixed 174 of 180 vulnerabilities in 263397 scanned packages
  6 vulnerabilities required manual review and could not be updated

残り6個はマニュアルレビューしてくれとかいてある。

⑤npm dedupeで重複したパッケージの整理統合をこころみる

npm dedupeを理解する

まず、dedupeの概念を理解するために以下の状態を考える

  • 自プロジェクトnpmパッケージAnpmパッケージBに依存している
  • npmパッケージAnpmパッケージC @2.1.1に依存している。
  • npmパッケージBnpmパッケージC @2.2.0に依存している。

この場合、npmを使っていると1npmパッケージC @2.1.1npmパッケージC @2.2.0もインストールされる。

パッケージとしてはnpmパッケージCで同じなのに、npmパッケージC @2.1.1npmパッケージC @2.2.0でバージョンが違いがあるので両方保持されしまう。

だったら、バージョンを新しいほうの@2.2.0のほうに合わせて、npmパッケージC @2.2.0のほうを、npmパッケージAnpmパッケージBで共通化して使いましょう、というのがdedupeの発想。

npm dedupeは新しいバージョンをインストールしてくれない

npm update等でdedupeはひととおりのパッケージを最新にした後にやる。
なぜなら、dedupe自身はパッケージの重複排除などはやってくれるが、最新のパッケージを入れてくれるわけではないので古いパッケージバージョンで状態でdedupeしてもあまり意味がない。

dedupeする

さて、ではnpm dedupe (npm ddpでもOK)してみる

removed 10 packages and audited 263298 packages in 3.622s
found 0 vulnerabilities

脆弱性が0になった!

なぜdedupeで脆弱性がゼロになった?

これは、dedupeが脆弱性を排除しているというより、古いパッケージバージョンに依存していたライブラリが新しいパッケージバージョンを参照するようにdedupeが変更してくれた効果のため。

上の例でいうと以下のようになる。

  • 自プロジェクトnpmパッケージAnpmパッケージBに依存している
  • npmパッケージAnpmパッケージC @2.1.1(脆弱性ありバージョン)に依存している。
  • npmパッケージBnpmパッケージC @2.2.0(安全バージョン)に依存している。

の状況をdedupeが↓のように変更してくれた効果

  • 自プロジェクトnpmパッケージAnpmパッケージBに依存している
  • npmパッケージAnpmパッケージC @2.2.0(安全バージョン)に依存している。
  • npmパッケージBnpmパッケージC @2.2.0(安全バージョン)に依存している。

⑦回帰テストを実行する

さてここまでで、脆弱性の無い状態にできたら、最後にテストをしてパッケージバージョンを変更したことによるプロジェクトのデグレが発生していないかどうかを確認する。

npm test

image.png

テスト無事通過!

(といっても、今回はdevDependenciesの依存のみだったけど)

これで対応完了!

途中でつまずいた場合

上述のコマンドだけでうまくいかず、つまずくことも多々ある。そうした場合は、コマンドだけで楽に突破できない状況になっている可能性があるので、それなりの調査分析工数を覚悟するしかない。

つまずき例

  • vulnerabilityが無くならなかった

    • 対応案:npm auditでvulnerabilityのあるパッケージに依存している上位のパッケージを特定する。そのパッケージがdeprecateになっていないか。ちゃんとメンテされているか確認する。deprecatedだったりメンテされていなければ使うのをやめたりPR送ってみたり、自分で直してみたり、代替パッケージを探したり。そういった工数を覚悟、確保する。
  • 最後のテストでつまづいた

    • 対応案:手順を1手ずつロールバック(手順を戻す)しながら、都度npm testを実行し、どの段階でつまづいたのか特定する。ありがちなのは②-1メジャーアップデートでAPIに破壊的変更

まとめ


  1. yarnは自動的にdedupeしてくれる(https://classic.yarnpkg.com/ja/docs/cli/dedupe/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

obnizとLineBotで防犯ツールを作ってみた(NO MORE XX 泥棒)

この記事を見てできること

LINEBOTに「スタート」とメッセージを送ると遠隔操作で監視をしてくれるものが作れます。
異常があったら必要以上にしつこく知らせてくれます。
(処理をストップさせる処理は書いてないです)

obnizの配線

配線はこんな感じ。
IMG_0140.JPG

コード内にも書いてありますが、人感センサーは{vcc:11, signal:10, gnd:9} につなぎます。

ソースコード

はじめobniz.onconnectをするタイミングでハマって動かなかった。
handleEventよりも先にobniz.onconnectをしておく必要がある。

line_sensor.js
'use strict';

// obniz呼び出し
const Obniz = require('obniz');
var obniz = new Obniz("XXXXXX");  // Obniz_ID に自分のIDを入れます

const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;

const config = {
    channelAccessToken: 'XXXXXX',
    channelSecret: 'XXXXXX'
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

// obniz接続//////////
// スコープ(変数の影響範囲)を最上部に置いておく
var sensor;
var led;

// 接続した段階でセンサーの準備はしておく
obniz.onconnect = async function(){
    obniz.display.clear();
    obniz.display.print("obniz LINE bots");
    sensor = obniz.wired("HC-SR505", {vcc:11, signal:10, gnd:9});
    led = obniz.wired("LED",{anode:0,cathode:1});
}

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  let mes = ''
  if(event.message.text === 'スタート'){
    mes = 'OK!監視始めるね!'; //メッセージだけ先に処理
        // ディスプレイ処理
        obniz.display.clear();  // 一旦クリアする
        obniz.display.print("Hello obniz!!!!!!");  // Hello obniz!という文字を出す

        // setIntervalで間隔を作る
        setInterval(async function(){
          // 非同期で取得
          var detected = await sensor.getWait();
          // console.log(detected);
          // displayに反映
          obniz.display.clear();  // 一旦クリアする
          // obniz.display.print(detected);  // 英語が出力できる
          // 近づいてきたら判定する
         if(detected == true ){ // 何かを感知したら
            obniz.display.clear();  // 一旦クリアする
            obniz.display.print("someone moving!");
            led.on(); // ライトON
            getAskObniz(event.source.userId);  // message
          }else{
            led.off();
          } 
        },1000); // 1000ミリ秒 = 1秒

  }else{ mes = event.message.text;}

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}

const getAskObniz = async (userId) => {
  await client.pushMessage(userId, {
      type: 'text',
      text: "誰かいるかも?",
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);

XXXXXXになっている箇所は自分のObniz IDや、channelAccessToken、channelSecretを入れることをお忘れなく。

ほぼ完成

ここまで機能としては完成です。こんな感じで動きます。
IMG_0141.jpg
人を感知すると知らせてくれます。

このままでは終われない

こういった手で触れるものは見た目も大事。そして遊び心も必要です(たぶん)

この時点で平日の夜中2時を回っている。当然明日も朝から仕事である。

考えても何もアイデア浮かばない。。
ティッシュがあるからてるてる坊主?
いや、最近ティッシュやトイレットペーパーがなかなか売ってないから無駄使いはダメだ...

「よし、映画館のあいつを作ろう」
IMG_0134.JPG

こんな感じで動きます

最後に

物理的に触れるものは愛着湧きますね。もっと時間がある時にもっとそれっぽく作りたい。
あと、LINEでストップとメッセージしたらモニタリングがストップするような処理も書きたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む