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

Node.jsに令和対応が来てた

Node.js 12.1.0には令和対応が入っている

Node.jsの最新版に令和対応が入っているようです。

2019-04-29, Version 12.1.0 (Current), @targos
intl:
Update ICU to 64.2. This adds support for Japanese Era (Reiwa) (Ujjwal Sharma) #27361.

https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V12.md#12.1.0

Ujjwal Sharmaさん、日本のことをよくご存じなんでしょうか…。

実際に試す

macOSの場合はHomebrewで最新版が降ってくるのですぐ試せます。

$ brew upgrade node
(略)
$ node -p "new Date().toLocaleDateString('ja-JP-u-ca-japanese', { era: 'long' })"
令和元年5月1日
$ node -p "new Date(Date.UTC(2019, 3, 30, 0, 0, 0)).toLocaleDateString('ja-JP-u-ca-japanese', { era: 'long' })"
平成31年4月30日

5月1日から先は令和元年となるようです。これでNode.jsで令和対応の仕事が入っても安心ですね!(そんな仕事があるかは知らない)

(おまけ)ブラウザの対応

手元のChromium 75、Firefox 66、Safari 12.1ではいずれも「平成31年5月1日」と表示されました。ブラウザの対応はもう少し先になりそうです。

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

Node.js で令和元年 (full-icu + toLocaleDateString)。

Node.js はデフォルトでは日本語関連の ICU データを持っていないので、別途インストールする必要があります。

> npm i full-icu

で、これを指定して実行してあげましょう。

> node -v
v12.1.0
> node --icu-data-dir=node_modules/full-icu -p "new Date().toLocaleDateString('ja-JP-u-ca-japanese', { era: 'long' })"
令和元年5月1日
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.jsでget,postのフォーム通信(express)

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var ejs = require('ejs');

app.engine('ejs', ejs.renderFile);
app.use(bodyParser.urlencoded({
extended:true
}));

app.get('/', (req, res) => {
console.log('--get req--');
console.log('name:' + req.query.name);
console.log('age:' + req.query.age);
res.render('temp.ejs', {});
});

app.post('/', (req, res) => {
console.log('--post req--');
console.log('name:' + req.body.name);
console.log('age:' + req.body.age);
res.render('temp.ejs', {});
});

var server = app.listen(1234, () => console.log('wakeUp'));

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

Lambda (Node.js 8) 等を Terraform で立ててサーバレスにAWSの課金額をSlackに自動通知する

背景

AWS を使っていると、単純に忘れたり、認識していないリソースがあったり、リソースの落とし忘れで
いつの間にか課金されていることが多い。 :cry:
意識しなくても把握できるように自動通知したい。 :point_up:

現状、Cloud Watch のアラートは閾値をこえたときに発報されるルールであるなど制限が多い。 :pensive:
利用者からすると、予算のある値を超えたときでは遅くて、
金額の傾向を知りたいので金額ベースでなく日次でほしい。 :grinning:

関連

拙項ですが、GCP 版

AWS 版については、下記記事を参考にさせていただいた。 :bow:

概要

今回は Lambda をメインに使って、 AWS 課金額を日次で Slack に通知できるようにする。

Lambda を採用したのは、 AWS サービス内なので権限管理しやすく、
また無料枠もあってランニングコストを抑えられるからである。 :thumbsup:

さらに、 Terraform と GitHub を構成管理に使うことで、そもそも何のリソースが何に使われているか把握しやすくする。

使用した Lambda のソースコードは
https://github.com/iijimakazuyuki/AWSBillingReport
に、 Terraform のコンフィギュレーションは本文中に記載している。

環境の説明

AWS 内のリソース管理は、今回は Terraform を使う。

GUI や CLI ベースだと手続き的になるので、手順が煩雑になりやすいためである。
Terraform を使うと、宣言的にリソースを記述できる。

Terraform を使う上では、コンフィギュレーションファイルを GitHub で管理し、
Atlantis によるワークフローを適用する。

Atlantis については、今回は深く触れない。 :bow:
GitHub でプルリクエストを作ると Terraform を動かしてくれる人がいる、というイメージ。 :muscle:
その人は、 AWS の強い権限を持っている前提で話を進める。

今回使用するリソースは、次の図のようになる:

+-----------    A     W     S    ------------+
| +-------------------+                      |
| | CloudWatch Events |                      |
| +-------------------+                      |
|    |                                       |
| execute                                    |
|    | +----+                                |
|    | | S3 |                                |
|    | +----+                                |
|    |   ^                                   |
|    |  refer                                |
|    v   |                                   |
| +--------+          +--------------------+ |
| | Lambda |--query-->| CloudWatch Metrics | |
| +--------+          +--------------------+ |
+---------|----------------------------------+
         post          \
          v             \
      +-------+    +-----------------------+   +--------+
      | Slack |    | Terraform    Atlantis |---| GitHub |
      +-------+    +-----------------------+   +--------+
  • Lambda の日次実行には、広く知られている通り CloudWatch Events ルールを用いる。
  • Lambda へのデプロイは S3 を通じて行う。

ステップ0: 必要な環境を準備する

  1. Slack に Incoming Webhook を作る。
  2. GitHub に Terraform 用コンフィギュレーションリポジトリを作る。
  3. Terraform 用コンフィギュレーションをプッシュして、プルリクエストを作成する。

次のようなコンフィギュレーションをプッシュする:

data "aws_iam_policy_document" "billing_report_assume_role_policy" {
  // Lambda の AssumeRole のポリシー。
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "billing_report" {
  // Lambda のロール。
  name               = "billingReport"
  assume_role_policy = "${data.aws_iam_policy_document.billing_report_assume_role_policy.json}"
}

data "aws_iam_policy_document" "billing_report" {
  // Lambda のロールに与えるポリシー。
  statement {
    effect = "Allow"

    // 過不足ありそうだが
    // とりあえず CloudWatch の Read っぽいことと、
    // Lambda の実行ログを出力する権限を与えている。
    actions = [
      "cloudwatch:Describe*",
      "cloudwatch:Get*",
      "cloudwatch:List*",
      "logs:CreateLogStream",
      "logs:PutLogEvents",
    ]

    resources = ["*"]
  }
}

resource "aws_iam_user" "billing_report_developer" {
  // 開発用に IAM ユーザを作成する。
  name = "billing_report_developer"
}

resource "aws_iam_user_policy" "billing_report_developer" {
  // 開発用 IAM ユーザに権限を付与する。
  name   = "billing_report_developer_policy"
  user   = "${aws_iam_user.billing_report_developer.name}"
  policy = "${data.aws_iam_policy_document.billing_report.json}"
}

resource "aws_iam_role_policy" "billing_report" {
  // Lambda のロールにポリシーを紐づけ。
  name   = "billing_report_policy"
  role   = "${aws_iam_role.billing_report.id}"
  policy = "${data.aws_iam_policy_document.billing_report.json}"
}

resource "aws_cloudwatch_event_rule" "billing_report" {
  // Lambda の日次実行のための CloudWatch Events ルール。
  name                = "billingReport"
  description         = "Send billing report daily"
  schedule_expression = "cron(0 0 * * ? *)"
}

Atlantis はプルリクエストを作成すると terraform plan を実行してくれ、
atlantis apply とコメントするとが実際に適用されて便利だが、
ともかく、 terraform apply まで実行できて、上記リソースが作成済みになった、とする。

Terraform で作成した IAM ユーザ billing_report_developer のアクセスキーが開発に必要なので、
IAM 管理画面 から作成し、メモする。

課金データのエクスポートは Terraform では設定できないようなので、 GUI から操作する。
参考:Amazon Web Services(AWS) で無料枠を超えた時にアラートを飛ばす(日本語メニュー)

ステップ1: ローカルで作ってみる

今回は私の好みで Node.js ランタイムを使用することにした。

まずはディレクトリを切って、

npm init

を実行し、適当に答えつつ Enter を連打する。
AWS SDK を使用するので、インストール:

npm install aws-sdk

このクライアントライブラリは、先ほどメモしたアクセスキーID、シークレットアクセスキーを環境変数で与えれば認証が通る。

export AWS_ACCESS_KEY_ID=<access_key_id>
export AWS_SECRET_ACCESS_KEY=<secret_access_key>

Lambda では、実行する関数を指定する。
開発中はテストを書いて実行するのがよいだろうが、今回は省略する。
Node.js の REPL を立ち上げて、下記のように実行して動作確認はできる。

> const index = require('./index.js');
undefined
> index.handler();
Promise {
  <pending>,
  domain:
   Domain {
     domain: null,
     _events: { error: [Function: debugDomainError] },
     _eventsCount: 1,
     _maxListeners: undefined,
     members: [] } }
>

ステップ2: デプロイする

ZIP で固めて S3 にアップロードする。
AWS SDK 以外を使用する場合、 node_modules 内も固める必要があるが、今回は不要なので、次のようにアップロードする。
なお開発用の IAM ユーザは権限が足りない。
アップロード可能な IAM ユーザを使うよう、環境変数を指定しなおす必要があることに注意。

$ zip v1.0.0.zip index.js
  adding: index.js (deflated 61%)
$ aws s3 cp v1.0.0.zip s3://<bucket_name>/v1.0.0.zip

次に、 Terraform 用コンフィギュレーションをプッシュして、プルリクエストを作成する。

次のようなコンフィギュレーションを追加してプッシュする:

resource "aws_lambda_function" "billing_report" {
  // Lambda 関数を ZIP で固めて S3 にアップロードしたものを参照させてデプロイする。
  s3_bucket     = "<bucket_name>"
  s3_key        = "v1.0.0.zip"
  function_name = "billing_report"
  role          = "${aws_iam_role.billing_report.arn}"
  handler       = "index.handler"
  runtime       = "nodejs8.10"

  environment {
    variables = {
      // Webhook URL を環境変数で与える。
      WEBHOOK_URL = "<webhook_url>"
    }
  }
}

resource "aws_cloudwatch_event_target" "billing_report" {
  // 日次実行の CloudWatch Events ルールで Lambda を実行する。
  target_id = "billingReport"
  arn       = "${aws_lambda_function.billing_report.arn}"
  rule      = "${aws_cloudwatch_event_rule.billing_report.name}"
}

resource "aws_lambda_permission" "billing_report" {
  // 日次実行の CloudWatch Events ルールで Lambda を実行させるのに必要なパーミッションを作成する。
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.billing_report.function_name}"
  principal     = "events.amazonaws.com"
  source_arn    = "${aws_cloudwatch_event_rule.billing_report.arn}"
}

これで、CloudWatch Events ルールが実行される毎日朝9時に Slack に次のようなメッセージが投稿される。

2019/4/30 0.39

CloudWatch Events ルールを手動で実行させることはできないようだが、
Lambda から適当なテストを実行すれば、その時点でメッセージを投稿させられる。

ステップ3: 機能改善

機能を追加したいとか、メッセージをもうちょっと丁寧にしたいとかで、アップデートしたい場合は、次のような手順を踏む。

  1. ソースコードを ZIP に固める。
  2. ZIP ファイルを S3 にアップロードする。バージョン名をつけるとわかりやすい。
  3. Terraform 用コンフィギュレーションをプッシュして、プルリクエストを作成する。

下記 <version> を更新して、 apply すればよい。

resource "aws_lambda_function" "billing_report" {
  // Lambda 関数を ZIP で固めて S3 にアップロードしたものを参照させてデプロイする。
  s3_bucket     = "<bucket_name>"
  s3_key        = "v<version>.zip"

  // (略)

まとめ

AWS の課金額を意識せずとも把握できるよう、日次で Slack に通知されるようにした。

  • その際、広く用いられている構成ではあるが、 Lambda を中心としたサーバレス構成とすることで、通知システム自体のランニングコストを抑えた。
  • Terraform (+ Atlantis) と GitHub を構成管理に使うことで、システムに使用しているリソースを把握しやすくした。

付録:躓いたところ

  • 何のリソースを作ればサービス間が連携できるのかが直ちには分からないし、今後どうしたらよいかもよく分かっていない。 Lambda パーミッションを作り忘れていることに気づかず、 Lambda 関数の GUI (Designer) に CloudWatch Events が表示されないなとしばらく悩んでいた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Arduinoをブラウザから動かした話

はじめに

Arduinoをブラウザから動かせたので後でまた再現できるようここにメモとして残したいと思います。

使用するもの

  • Arduino Uno
  • Arduino IDE
  • Node.js
  • Google chrome
  • LED
  • 抵抗100Ω
  • ジャンパー線
  • ブレッドボード
  • USB

こんなところですかね?

Hello World

電子創作のHello WorldはLチカです。
が、この記事では飛ばします。
JavaScriptエンジニアへのIoTのすすめ:Node.jsとArduinoでスマートデバイスのプロトタイプをしてみよう
を見てください。

注意点ですが、上の記事通りに進めてもWindowsではエラーを吐かれます。

var five = require('johnny-five');
var board = new five.Board();

のところを

var five = require("johnny-five");
var board = new five.Board({port:%ポート番号%});

にしないとダメです。

ポート番号はArduino IDEの『ツール→シリアルポート』から確認できます。

例えば、シリアルポートが COM1 であれば、

var five = require("johnny-five");
var board = new five.Board({port:"COM1"});

になります。

また、 npm install johnny-fiveに加え、npm install firmataも実行する必要があります。

Ctrl + c二回で止めることが出来ます。光った状態で二回目を押すと、光った状態のまま止まってしまうので注意です。

Arduinoをブラウザから制御する

この記事(メモ)の本題です。
今回はExpressを使ってルーティングや値の受け渡し、レンダリングなどのあれこれを行い、ejsファイルでDOMを構成しました。

まず、先ほどのLチカとは別にディレクトリ(ここではこれをArduino_web_operateとします)をつくり、まずnpm initを実行します。色々聞かれますが、全部エンターしました。
そのあと以下のコマンドを実行しました。

- npm install --save express
- npm install --save ejs
- npm install --save body-parser
- npm install johnny-five
- npm install firmata

次にArduino_web_operateの中にpublicファイルを作成し、その中にstyle.cssを作成し、以下のコードを記述しました。

style.css
h1 {
    font-size: 128px;
    font-weight: bold;
    color: #f0f0f0;
    text-align: center;
    letter-spacing: -10px;
    margin: -50px 0px -140px 0px;
}

次にArduino_web_operateの中にviewsファイルを作成し、その中にindex.ejsを作成しました。

index.ejs
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html" charset="utf-8">
        <title>NodeWebOperate</title>
        <link type="text/css" href="./style.css" rel="stylesheet">
    </head>
    <body>
        <head><h1>イエッタイガー!</h1></head>
        <main>
            <form action="/" method="post">
                <p>
                    <input type="radio" value="on" name="operate">ON
                    <input type="radio" value="off" name="operate">OFF
                    <input type="submit" value="送信">
                </p>
            </form>
            <p><%-content %></p>
        </main>
    </body>
</html>

次にArduino_web_operateの中にindex.jsを作成しました。
ポートはCOM1, ピンは12番に繋ぐものとします。また、回路はLチカと同じです。

index.js
const express = require("express");
const five = require("johnny-five");
const board = new five.Board({port:"COM1"});

const app = express();
app.use(express.static("public"));

const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({extended: false}));

let led;

board.on('ready', function() {
    led = new five.Led(12);
});

const message = "現在の状態:";

app.get("/", (req, res)=>{
    res.render("index.ejs",{
        content: message + "off",
    });
});

app.post("/", (req, res)=>{
    if(req.body.operate == "on"){
        led.on();
    }
    if(req.body.operate == "off"){
        led.off();
    }
    res.render("index.ejs",{
        content: message + req.body.operate,
    });
});

const server = app.listen(3000, ()=>console.log("イエッタイガー!"));

その光る様からイエッタイガーと叫ばせました。
参考:視覚的イェッタイガーとは?
node index.jsを実行して、localhostの3000にアクセスして、いじってみてください。onを押して送信したら光る/offを押して送信したら暗くなるなら成功です。お疲れさまでした。以上です。

終わりだよ~

参考

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

nodenvでバージョン指定してNode.jsインストールする(mac)

インストール

nodenvのインストール

nodenv公式にあるとおり進める
https://github.com/nodenv/nodenv

$ brew install nodenv
$ nodenv init
$ echo 'eval "$(nodenv init -)"' >> ~/.bash_profile

Node.jsのインストール

インストールできるバージョンの確認、インストール

$ nodenv install -l
Available versions:
  0.1.14
  0.1.15
  0.1.16
 (略)

今回は推奨版の10.15.3をインストールする

推奨版のバージョンはNode.js公式で確認できる
https://nodejs.org/ja/

$ nodenv install 10.15.3
$ nodenv rehash
$ nodenv global 10.15.3

インストール、バージョン指定できているか確認

$ nodenv versions
* 10.15.3 (set by /Users/kanako/.nodenv/version)
$ node -v
v10.15.3
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node-RED Alexa Home Skill Bridgeを設定する(2019/1Q版 その1)[On Onion Omega2+]

image.png

昨年10月(2018/10)の記事「Node-REDからpython-broadlinkでRM mini3をコントロール【失敗】[on Onion Omega2+]」では、python-broadlinkNode-Redexecノードで実行しようというものでした。結果的に75-exec.jsTypeErrorの例外が発生してしまい、躓いていました。
原因は、使っているNode.jsが古い(v4.3.1)ことが起因しているようでした・・・
Node.jsをクロスコンパイルすることもトライしましたが、ハードルが高く断念しました。

あれから約半年、どうもNode.js v8.10.0が正式サポートされたようです。
また、Node-REDも公式にサポートされました。但し、公式サポートは、最新のOmega2 Proのようです。Omega2+と比較がありましたので、参照すると以下のようになっておりました。MemoryがOmega2 Pro=512MBに対し、Omega2 Plus=128MBとなっておりますが、Swapで対応すれば、カバーできそうです。

image.png

0. 前提条件

  • Onion Omega2 Plus ※工場出荷状態にリセット済み
  • 2GB Micro-SD Card
    • パーティション2分割
      • 512MB=スワップ用、残り=システム領域拡張用
      • 各領域共にext4でフォーマット済み
  • Omega2拡張ドック

1. ファームウェアのアップデート

最初にファームウェアをアップデートします。
詳細は、公式ページを参照してください。

$ oupgrade -l

更新後のファームウェアの確認は、以下で可能です。

$ oupgrade -v
> Device Firmware Version: 0.3.2 b220
$

この時点で以下の構成となります。

  • Onion Omega2 Plus
    • BusyBox v1.28.3
    • Ω-ware: 0.3.2 b220
    • Linux 4.14.81 #0 Thu Feb 21 20:59:23 2019 mips GNU/Linux

2. スワップ領域の確保

以前の記事に書いた方法でスワップ領域を作成・設定します。

必要なパッケージのインストール

$ opkg update
$ opkg install swap-utils block-mount

スワップファイルの作成

$ dd if=/dev/zero of=/mnt/mmcblk0p1/swap.page bs=1M count=512

スワップ領域として設定・有効化

$ mkswap /mnt/mmcblk0p1/swap.page
$ swapon /mnt/mmcblk0p1/swap.page

micro-SD自動マウント有効化

$ block detect > /etc/config/fstab

/etc/config/fstabを開いて、option enabled'1'に設定します。

/etc/config/fstab
config 'mount'
        option  target  '/mnt/mmcblk0p1'
        option  uuid    'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
        option  enabled '1'

スワップファイル自動有効化

/etc/rc.local に swapon コマンドを仕込みます。

/etc/rc.local
# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.

### activate the swap file on an external SD-Card
SWAP_FILE="/mnt/mmcblk0p1/swap.page"
if [ -e "$SWAP_FILE" ]; then
        swapon $SWAP_FILE
fi

exit 0

再起動して、スワップファイルが自動で有効になっているか確認します。

$ free
             total       used       free     shared    buffers     cached
Mem:        124920      93244      31676         44       5760      20828
-/+ buffers/cache:      66656      58264
Swap:       524284        512     523772

3. 外部ストレージからの起動設定

こちらも同じく、以前の記事に書いた方法で設定していきます。

外部ストレージのフォーマット

e2fsprogsをインストールします。

$ opkg update
$ opkg install e2fsprogs

アンマウント後、ext4でフォーマットします。
フォーマット後、再マウントします。

$ umount /mnt/mmcblk0p2
$ mkfs.ext4 /dev/mmcblk0p2

/overlay ディレクトリをコピー

/overlay に格納されているシステム関連ファイルを Micro-SD にコピーします。

$ mount /dev/mmcblk0p2 /mnt/ ; tar -C /overlay -cvf - . | tar -C /mnt/ -xf - ; umount /mnt/

/overlay 自動マウント設定

$ block detect > /etc/config/fstab

/etc/config/fstab のoption target'/overlay'に、option enabled'1'に設定します。

/etc/config/fstab
config 'mount'
        option  target  '/overlay'
        option  uuid    'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
        option  enabled '1'

設定が終わったら再起動します。

以下、確認結果です。

$ df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/root                 7936      7936         0 100% /rom
tmpfs                    62460        92     62368   0% /tmp
/dev/mmcblk0p2         1348020    291676    969816  23% /overlay
overlayfs:/overlay     1348020    291676    969816  23% /
tmpfs                      512         0       512   0% /dev
/dev/mmcblk0p1          564156    524736         0 100% /mnt/mmcblk0p1

4. Node-RED関連インストール

公式ガイドに従い、Node-REDをインストールします。

Node-Redインストール

公式ガイドを見ると、インストールはブラウザからアクセスするOnionOSのApp Managerで可能との記載があります。
OnionでのNode-RED公式サポートは、Omega2 ProのみOmega2+は公式サポートしていないようです。App Managerのインストール用アイコンには"Pro"と明記されています。インストールに際し、もしかしたらエラーと成り得る可能性も考え、(私的にエラー調査し易い)コマンドラインでインストールすることにしました。

$ opkg update
$ opkg install oos-app-nodered

インストール完了後、再起動すると自動的にNode-REDが起動するように/etc/init.d/nodered及び、/etc/rc.d/S99noderedが作成されるようです。

image.png

再起動後、Omega2にブラウザでアクセスすると、Node-REDのアイコンが追加されています。アイコンをクリックすると、Node-REDのフローエディタにアクセスできます。
image.png

ちなみに、起動後停止する場合は、/etc/init.d/nodered stopをコンソールから入力します。stopのところをstartで再開となります。

$ /etc/init.d/nodered stop
引数 説明
start サービスを起動する
stop サービスを停止する
restart サービスを再起動する
reload サービスの設定をリロードする(Node-RED未実装?)
enable サービスの自動起動を有効化する
disable サービスの自動起動を無効化する

サービスを利用せず、nodeコマンドで起動する場合は、以下となります。

$ /usr/bin/node --max-old-space-size=128 /usr/lib/node_modules/node-red/red.js --userDir /root/.node-red

以下、上記コマンドで実行した際、コンソール出力されたログに出力されるバージョン情報を記録しておきます。

Item Version
Node-RED version: v0.19.5
Node.js version: v8.10.0
Linux 4.14.81 mipsel LE

関連ツールのインストール

公式ガイドに従い、追加の関連ツールをインストールします。

まずは、npmをインストールします。

$ opkg update
$ opkg install node-npm

次にコンパイルが必要なNodeパッケージのインストールに備え、gccとmakeをインストールします。インストールにあたり、node-gypのインストールと設定が必要です。

$ opkg update ; opkg install python
$ cd /root/.node-red
$ node --max_old_space_size=512 $(which npm) install -g node-gyp

/etc/opkg/distfeeds.confのコメントアウトしてある"src/gz openwrt_base http://.....""src/gz openwrt_packages http://....."のリポジトリを有効化します。

/etc/opkg/distfeeds.conf
#src/gz openwrt_core http://downloads.openwrt.org/releases/18.06-SNAPSHOT/targets/ramips/mt76x8/packages
- #src/gz openwrt_base http://downloads.openwrt.org/releases/18.06-SNAPSHOT/packages/mipsel_24kc/base
+ src/gz openwrt_base http://downloads.openwrt.org/releases/18.06-SNAPSHOT/packages/mipsel_24kc/base
#src/gz openwrt_luci http://downloads.openwrt.org/releases/18.06-SNAPSHOT/packages/mipsel_24kc/luci
#src/gz openwrt_onion http://downloads.openwrt.org/releases/18.06-SNAPSHOT/packages/mipsel_24kc/onion
- #src/gz openwrt_packages http://downloads.openwrt.org/releases/18.06-SNAPSHOT/packages/mipsel_24kc/packages
+ src/gz openwrt_packages http://downloads.openwrt.org/releases/18.06-SNAPSHOT/packages/mipsel_24kc/packages
#src/gz openwrt_routing http://downloads.openwrt.org/releases/18.06-SNAPSHOT/packages/mipsel_24kc/routing
#src/gz openwrt_telephony http://downloads.openwrt.org/releases/18.06-SNAPSHOT/packages/mipsel_24kc/telephony
src/gz omega2_core http://repo.onioniot.com/omega2/packages/core
src/gz omega2_base http://repo.onioniot.com/omega2/packages/base
src/gz omega2_packages http://repo.onioniot.com/omega2/packages/packages
src/gz omega2_routing http://repo.onioniot.com/omega2/packages/routing
src/gz omega2_onion http://repo.onioniot.com/omega2/packages/onion

/etc/opkg/distfeeds.confを保存後、以下を実行します。

$ opkg update
$ opkg install make gcc
$ ar -rc /usr/lib/libpthread.a

Node-RED認証有効化

Node-REDの認証を有効化します。
【2019/04/29時点】
 有効化できますが、bcryptjsが遅いためか(?)認証が上手くいきませんでした。
 現状、無効化してます。

**/root/.node-red認証時に使用するモジュール(bcryptjs)をインストールします。

/root/.node-red
$ cd /root/.node-red
$ node --max_old_space_size=512 $(which npm) install bcryptjs

認証用パスワードのハッシュコードを得ます。
(ハッシュコードが得られるまで約3分かかります)

/root/.node-red
$ cd /root/.node-red
$ node -e "console.log(require('bcryptjs').hashSync(process.argv[1], 8));" your-passwd
$2a$08$8KgbG3bxk9Dg0cJUw/i2oOQf72zhcn9nUzqp6m.jJ8DF4X/b6cU82

ハッシュコードが得られたら、/root/.node-red/settings.jsの122行目あたりにあるadminAuth部分を有効化(コメントアウトを削除)してpasswordの部分に取得したハッシュコードを定義します。

/root/.node-red/settings.js
   adminAuth: {
        type: "credentials",
        users: [{
            username: "admin",
            password: "$2a$08$8KgbG3bxk9Dg0cJUw/i2oOQf72zhcn9nUzqp6m.jJ8DF4X/b6cU82",
            permissions: "*"
        }]
    },

Node-REDを再起動し、ブラウザでアクセスすると、以下のようにユーザ名とパスワードを入力する画面が表示されます。
【2019/04/29時点】認証でFailedになっちゃいます。

image.png

とりあえず、一旦休憩!
Node-REDインストール迄を「その1」とします。

次回は、

  • broadlink RM mini3の設定
  • Alexa Home Skill Bridgeの設定

を行っていく予定です。

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

HTTP Cloud Functions for Firebase で HTTP リクエストの発信元を認証する方法

こんにちは! 本日より 令和 時代の幕開けです?㊗️

ふだん Golang を主に使っていて Node.js は、はろーわーるど程度の戦闘力しかない @wezardnet です?
現在は Node.js 以外の言語もサポートされている Cloud Functions ですが、この機会に学習も兼ねて体験してみようと思います。

さて、今年もまた Google Cloud Next ’19 に行くことができなかったので GCPUG の報告会に行ってきましたー?

0. 前提条件

  • Google Cloud Platform(GCP) のプロジェクトが作成されている
  • Google Developer Console で Cloud Functions API が有効化されている
  • Cloud Functions for Firebase に必要な環境(各種 SDK などもろもろ)が整っている

1. Cloud Functions HTTP Trigger

Cloud Functions の HTTP トリガーは URL さえ知っていれば、誰でも実行できてしまいます。しかしながら、特定の発信元だけにトリガーの実行を許可したい場合はちょっと困りますよね。。。?

実は HTTP リクエストの発信元を認証してトリガー実行を制限する方法は公式で紹介されてますので、まずは HTTP Cloud Function での認証 をお読みください。仕組みとしては Cloud Storage のバケットを承認プロキシに利用するようです。バケットに対して読み取り権限を付与したサービス アカウントを用意し、そのアクセス トークンで認証させる格好になります。

本記事では、この方法を HTTP Cloud Functions for Firebase として実装した場合について書いています。

2. サービス アカウントを作成する

Google Developer Console から IAM & admin の Service accounts を選択します。
サービス アカウントを作る時は、何の目的で使われるモノなのか後で分かるように名前や説明をきちんと付けましょう。今回は「functions_service_account」という名前にし、説明に「Cloud Functions Http Trigger Account」として作りました。

image02.png

サービス アカウントが作られたら、ケバブメニューアイコンからキーを作ります。

image01.png

JSON 形式で認証情報をダウンロードします。ダウンロードした JSON ファイルはアクセス トークンを入手する際に使うので大切に保管しておきましょう。

3. Cloud Storage にバケットを作る

Google Developer Console から Cloud Storage を選択し、認証用のバケットを作ります。バケット内は で使用するため、ストレージクラスは気にしなくて良いです?

image01.png

バケットが作られたら、バケットのアクセス権限(パーミッション)に前項 2. で作成したサービス アカウントを追加します。バケットに対する読み取り権限さえあれば良いので Storage Legacy Bucket Reader ロールを付与します。

image01.png

4. 関数の作成

はじめに以下のコマンドで初期設定を行います。

$ firebase init functions

基本は公式の HTTP Cloud Function での認証 で紹介されている関数 secureFunction をそのまま流用していますが for Firebase ということで少々アレンジを加えてます。また、せっかくなので Express フレームワークを使ってルーティングさせるようにしました。これで各種 API を作ることができますね!

./functions/index.js
const functions = require('firebase-functions');
const express = require('express');
const app = express();
const google = require('googleapis').google;
const storage = google.storage('v1');
const BUCKET = '{BUCKET_NAME}';

const getAccessToken = function (header) {
    if (!header) return null;

    const match = header.match(/^Bearer\s+([^\s]+)$/);
    return match ? match[1] : null;
}

app.all('*', (req, res, next) => {
    const accessToken = getAccessToken(req.get('Authorization'));
    // console.log('accessToken = ' + accessToken);

    const oauth = new google.auth.OAuth2();
    oauth.setCredentials({ access_token: accessToken });

    const permission = 'storage.buckets.get';
    storage.buckets.testIamPermissions(
        { bucket: BUCKET, permissions: [permission], auth: oauth }, {}, (_, response) => {
            // console.log('response = ' + JSON.stringify(response));
            if (typeof response !== 'undefined' 
                    && (response.data && response.data['permissions'] && response.data['permissions'].includes(permission))) {
                return next();
            } else {
                res.status(403).send('The request is forbidden.');
            }
        });
});

app.get('/sample', (req, res) => {
    const data = [
        { "id": 1, "name": "ねこぽん" },
        { "id": 2, "name": "うさぽん" },
        { "id": 5, "name": "ぺんぺん" }
    ];
    res.send(JSON.stringify(data));
});

const api = functions.https.onRequest(app);
module.exports = { api };

参考までに package.json も載せておきます。

./functions/package.json
{
    "name": "functions",
    "description": "Cloud Functions for Firebase",
    "engines": {"node": "8"},
    "scripts": {
        "lint": "eslint .",
        "serve": "firebase serve --only functions",
        "shell": "firebase functions:shell",
        "start": "npm run shell",
        "deploy": "firebase deploy --only functions",
        "logs": "firebase functions:log"
    },
    "dependencies": {
        "express": "^4.16.4",
        "firebase-admin": "~7.0.0",
        "firebase-functions": "^2.2.0",
        "googleapis": "^39.1.0"
    },
    "devDependencies": {
        "eslint": "^5.12.0",
        "eslint-plugin-promise": "^4.0.1"
    },
    "private": true
}

Node.js ではコールバック地獄に陥らないように頭を悩ませますね。

5. 関数をデプロイする

次のコマンドを実行してデプロイします。

$ firebase deploy --only functions

実際に関数がデプロイされてるかどうか Google Developer Console から Cloud Funcrions を選択して確認します。割り当てメモリはデフォルトで 256 MB になるので、あとから自分で 128 MB に変更しましたが firebase コマンドではデプロイ時に指定できないのでしょうか??

image03.png

Firebase Console の場合は次のように表示されます。

image02.png

6. 動作を検証する

意図したとおりに関数が動作するか検証します。まずはサービス アカウントを使ってアクセス トークンを入手するために、前項 2. でダウンロードした認証情報(JSON)を使ってアクセス トークンを取得します。今回は curl で動作検証するので環境変数にトークンを格納しておくと楽ちんです♫

6.1. アクセス トークンを環境変数に格納する

gcloud コマンドで取得します。

$ export ACCESS_TOKEN=$(GOOGLE_APPLICATION_CREDENTIALS=./functions_service_account.json gcloud auth application-default print-access-token)

実際にアクセス トークンが環境変数にセットされているか確認します。

$ echo $ACCESS_TOKEN

6.2. アクセス トークン無しでリクエストを投げる

まずは素のまま curl を実行してみます。期待どおりの結果(403)が返ってきます?

$ curl -w "\n" https://us-central1-{Project ID}.cloudfunctions.net/api/sample/

The request is forbidden.

6.3. アクセス トークン有りでリクエストを投げる

次にアクセス トークンを付与して curl を実行します。ちゃんと用意した JSON レスポンスが返ってきましたょ?

$ curl -w "\n" https://us-central1-{Project ID}.cloudfunctions.net/api/sample/ -H "Authorization: Bearer "$ACCESS_TOKEN

[{"id":1,"name":"ねこぽん"},{"id":2,"name":"うさぽん"},{"id":5,"name":"ぺんぺん"}]

7. アクセス トークンの有効期限

公式ドキュメントにも説明されているとおり、アクセス トークンには有効期限があります。本記事の方法でトークンを取得した場合は 1 時間で期限が切れます。ちなみに期限切れのトークンでリクエストを投げたところ 403 で弾かれ正しく挙動できています。

以下のコマンドでアクセス トークンに関する情報を取得することができます。

$ curl -w "\n" https://oauth2.googleapis.com/tokeninfo?access_token=$ACCESS_TOKEN

{
  "azp": "106234950138847254616",
  "aud": "106234950138847254616",
  "scope": "https://www.googleapis.com/auth/cloud-platform",
  "exp": "1554774775",
  "expires_in": "3588",
  "access_type": "offline"
}

expires_in がトークンの有効期限を示します(単位は秒)。

〜あとがき〜

昭和、平成に続いて令和という時代の移り変わりと共にテクノロジーや開発手法も日々進化しています。今流行りの技術も、何年も経たないうちに廃れるぐらい目まぐるしく変化している時代ですが、これからも 学び を忘れず成長していけたらと思います。。。

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

初のnpmパッケージ公開で行ったこと

はじめまして!
@Leopard0505で活動しているものです。
初めての投稿で分かりにくい点があると思いますが、指摘してもらえるとありがたいです。

完成物

こちらが今回作成したnpmパッケージです。
@leopard0505/payment-request
随時アップグレードしていきますので、よろしくお願いします。

公開までの手順

こちらが大変参考になりました。

npmアカウント作成

まずは、npm set コマンドを使って登録します。

author情報を作成
$ npm set init.author.name "Leopard0505"
$ npm set init.author.email "Leopard0505@example.com"
$ npm set init.author.url "https://qiita.com/Leopard0505"

次に npm adduser コマンドを使ってユーザを登録します。

npmユーザの登録
$ npm adduser
## まだユーザが登録されていない場合は、npm に登録する新規ユーザ情報を入力する。
## 既にnpm ユーザが作成されている場合はそのユーザの情報を入れて、ログインする。

ローカルからnpmにログイン

Webページ上(npm公式>signup)でアカウントを作成したら、続いてローカル環境のnpmコマンドから作成したアカウントにログインする。

ログイン自体は簡単で、以下のコマンドを実行すればよい。

npmにログイン
$ npm login

ユーザー名とパスワードとメールアドレスを聞かれるので、アカウント作成時に入力した値を入れる。

ファイルの準備

次に、公開するファイルを準備します。

最小限のディレクトリ構造
srcDir/
  +-- index.js
  +-- package.json
  +-- LICENSE.txt
  +-- README.me
  +-- .gitignore
  +-- .npmignore

npmに公開

公開用のファイルの準備まで終えたら、後は「公開」する。
「npm login」済みの環境で、以下を実行するだけ。

$ npm publish

これで、npmパッケージの公開は完了です。
任意のフォルダ上のコマンドラインから、npm install 「公開したパッケージ名」 を打ち込んでライブラリがインストールされるか確認しましょう。

お疲れ様でした!

今回の感想

初めてのnpmパッケージ公開でしたが、意外と簡単でした。
それよりも何を作るかで結構悩みました。
そんな感じで行き当りばったりで公開までしてみましたがなんとかなるもんですね。

10連休に入ってやりたいことをやれたので一先ず落ち着けました。
これからも積極的にアウトプットを行っていきたいと思います。

参考リンク

以下のリンクが大変参考になりました。
ありがとうございました。

@TsutomuNakamuraさん 初めてのnpm パッケージ公開
@hoshimadoさん npmでパッケージを公開してみた手順の記録

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