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

LINEBotのリッチメニューエディタがないので自作した

LINEBotで、以下のようなリッチメニューを表示します。今回はそのエディタと表示方法についてです。

image.png

リッチメニューは、トーク画面に表示されるメニューで、自分で自由に画像を作成できます。
HTMLでいうところのクリッカブルマップに相当し、自作する画像ファイルの範囲内で、四角形で範囲を指定し、そこをタッチしたときに特定の動作を行うことができます。
特定の動作として以下が可能です。

  • ポストバックアクション
  • メッセージアクション
  • URIアクション
  • 日時選択アクション

(参考) LINEリファレンス:リッチメニュー
https://developers.line.biz/ja/reference/messaging-api/#rich-menu

ソースコード一式をGitHubに上げておきました。

poruruba/LinebotRichmenu
 https://github.com/poruruba/LinebotRichmenu

トーク画面へのリッチメニューの設定

リッチメニューは、以下の3種類の設定箇所があり、いずれにも設定した場合、上の方が優先度が高いです。

  • Messaging APIで設定するユーザー単位のリッチメニュー
  • Messaging APIで設定するデフォルトのリッチメニュー
  • LINE Official Account Managerで設定するデフォルトのリッチメニュー

3つ目は、LINE Developersのページから設定可能です。

LINE Official Account Manager
 https://manager.line.biz/

アカウントを選択し、メッセージアイテム→リッチメッセージと選択すると、管理画面が表示されます。ただし、テンプレートが少なかったり、選択範囲が固定されていたりと、使いにくかったので、今回は使いません。今回は、前者2つのMessaging APIで設定するリッチメニューを扱います。

(i) Messaging APIで設定するデフォルトのリッチメニュー
これが一番簡単で、最初に一回だけAPIを呼び出せば完了です。設定したいリッチメニューの識別子(後述)を設定します。
すべての友達に同じリッチメニューが表示されます。

api\controllers\linebot-richmenu\index.js
app.client.setDefaultRichMenu(richmenu_id);

app.clientは、LINE Bot用の自作のライブラリ「line-utils.js」です。内部で以下のnpmモジュールを使っています。

line/line-bot-sdk-nodejs
 https://github.com/line/line-bot-sdk-nodejs

(ii) Messaging APIで設定するユーザー単位のリッチメニュー
ユーザごとに異なるリッチメニューを表示します。

api\controllers\linebot-richmenu\index.js
  await client.linkRichMenuToUser(event.source.userId, richmenu_id);

例えば、友達になったときに呼び出せばよいでしょう。
以下のような感じです。

api\controllers\linebot-richmenu\index.js
app.follow(async (event, client) =>{
  await client.linkRichMenuToUser(event.source.userId, richmenu_id);
});

以上により、トーク画面でリッチメニューが表示されるようになります。

リッチメニューの作成

で、じゃあリッチメニューはどうやって作成して、どうやってリッチメニューのIDを取得するのか。
それには、HTTP Post等で、設定します。

(参考) LINE Messaging APIリファレンス:リッチメニュー
https://developers.line.biz/ja/reference/messaging-api/#rich-menu

1点注意がありまして、CORS対応が必要なため、ブラウザのJavascriptから直接は呼び出せません。そこで、Node.jsサーバを立ち上げて、Javascriptからの要求を受け取って登録を行うようにします。

Node.js側での実装は、linebot sdkのnpmモジュールですでに関数化されているためそれを呼び出せばよいだけです。

createRichMenu(richMenu: RichMenu): Promise<string>
setRichMenuImage(richMenuId: string, data: Buffer | Readable, contentType?: string): Promise<any>

※他のAPIは以下を参照してください。
https://github.com/line/line-bot-sdk-nodejs/blob/next/docs/api-reference/client.md

①で、クリッカブルの位置情報を含めてリッチメニューとして登録し、リッチメニューIDを取得します。
ただし、このままでは画像ファイルは登録されていないため、続けて②で画像ファイルを登録して完了です。

ちなみに、一度登録したら変更できないため、修正後の新しいリッチメニューおよび画像ファイルを登録したのち、古いリッチメニューおよび画像ファイルを削除することになります。

画像ファイルのフォーマット(JpgかPNGか)を判別するためとStream操作のために、以下のnpmモジュールも使っています。

sindresorhus/file-type
 https://github.com/sindresorhus/file-type

paulja/memory-streams-js
 https://github.com/paulja/memory-streams-js

以下の部分を、環境に合わせて設定してください。LINE Developersコンソールから確認できます。(LINE Developers コンソール: https://developers.line.biz/ )

api\controllers\linebot-richmenu\index.js
const config = {
  channelAccessToken: '【チャネルアクセストークン(長期)】',
  channelSecret: '【チャネルシークレット】',
};

リッチメニューのクリッカブルマップに相当する部分は、以下に示すJSONフォーマットである必要があります。

Messaging APIリファレンス:リッチメニューオブジェクト
 https://developers.line.biz/ja/reference/messaging-api/#rich-menu-object

もろもろのリッチメニュー作成・登録作業をWebページから行えるようにエディタを作成しておきました。

image.png

画像ファイルをアップロードして可視化し、位置情報とアクション内容を追加して、最後にセットで登録することができます。登録が完了すると、リッチメニューIDが払い出されるので、それをLINEBotサーバに設定しましょう。

クリッカブルマップのように、X座標とY座標と、幅と高さを指定し、そこにクリックされたときのアクションを割り当てます。以下のアクションを選択して設定します。
・ポストバックアクション
・メッセージアクション
・URIアクション
・日時選択アクション
入力した範囲(X座標とY座標と幅と高さ)は、画像内に四角枠で示すようにしています。

また、何のリッチメニューを登録したか忘れてしまうので、すでに登録済みのリッチメニューや画像ファイルを参照できるようにしておきました。また、すでに登録済みのリッチメニューから複製して編集登録できるようにもしておきました。

以下の部分は、立ち上げたNode.jsサーバのURLに変えてください。

public\linebot-richmenu-edit\js\start.js
const base_url = "【Node.jsサーバのURL】";

終わりに

LINEBotに関する投稿に関しては、以下も参考にしてください。LINEボットサーバを立ち上げる手順を示しています。
 LINEボットを立ち上げるまで。LINEビーコンも。

以上

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

Node.jsをOpenTelemetryでトレーシングしてみる

はじめに

Node.js(JavaScript)でOpenTelemetryを扱う記事が少ないため、トレーシングまわりの基本的なノウハウをまとめてみました。
本記事では、Instrumentationを使用した自動トレース収集、startSpan()メソッド呼び出しによる手動トレース収集まわりを扱います。

OpenTelemetryとは?

マイクロサービスのような分散環境で、トレースやメトリクスを計測するフレームワーク。
可視化ツール(ZipkinやJaegerなど)と組み合わせることで、どの処理でどれだけ時間がかかっているのか解析しやすくなります。

おおまかな特徴
* CNCFがOpenTelemetryプロジェクトをホストしている(2015年設立)
* 従来のフレームワークに見られたベンダロックインを排除したオープンさがコンセプト
* 言語ごとにライブラリを提供
* 使用するライブラリを差し替えれば、手続きはそのままに、異なるツールとの連携が可能になる

やってみること

クライアント・サーバからトレースデータを集め、Zipkinでグラフ化してみます。
サンプルとして、クライアントが2種類のREST-APIを呼び出す構成を対象に行います。

architecture.png

実行環境

  • MacOS X
  • node: v13.8.0
  • npm: v7.5.3
  • express: v4.17.1
  • OpenTelemetry: v0.17

1. サンプル構成の作成

まずはベースとなるクライアント・サーバのサンプルを作成します。

1.1 サンプル作成

プロジェクトにExpressとAxiosをインストールします。

$ npm install express axios

下表に示す3種類のソースファイルを作成します。

ファイル名 実装内容
api1server.js API1のリクエストを受け付け、2秒後にレスポンスを返す
api2server.js API1のリクエストを受け付け、1秒後にレスポンスを返す
client.js API1、API2を順々に同期的に呼び出す
api-server1.js
'use strict';

const express = require('express');
const app = express();
const PORT = 8180;

const data = { name: "orange", price: 200};

async function setupRoutes() {
  app.use(express.json());

  app.get('/api/price', async (req, res) => {

    // 2秒後にレスポンスを返す
    setTimeout(() => {
      res.json(data);
    }, 2000);
  });
}

setupRoutes().then(() => {
  app.listen(PORT);
  console.log(`Listening on http://localhost:${PORT}`);
});
api-server2.js
'use strict';

const express = require('express');
const app = express();
const PORT = 8280;

const data = { name: "melon", price: 600};

async function setupRoutes() {
  app.use(express.json());

  app.get('/api/price', async (req, res) => {

    // 1秒後にレスポンスを返す
    setTimeout(() => {
      res.json(data);
    }, 1000);
  });
}

setupRoutes().then(() => {
  app.listen(PORT);
  console.log(`Listening on http://localhost:${PORT}`);
});
client.js
'use strict';

const axios = require('axios').default;

async function makeRequest() {

  // 1つ目のAPIを呼び出す
  await axios.get('http://localhost:8180/api/price')
  .then(res => console.log("API1: OK"))
  .catch(err => console.log("API1: ERROR"));

  // 2つ目のAPIを呼び出す
  await axios.get('http://localhost:8280/api/price')
  .then(res => console.log("API2: OK"))
  .catch(err => console.log("API2: ERROR"));

  // 5秒後に終了する
  setTimeout(() => { 
    console.log('Completed.');
  }, 5000);
}

makeRequest();

1.2 サンプルの動作確認

以下の手順で、2種類のサーバを起動し、Clientを起動します。
API1とAPI2を順々に呼び出し、それぞれ結果OKとなるはずです。

# Server1の起動
$ node api-server1.js 
Listening on http://localhost:8180

# Server2の起動
$ node api-server2.js 
Listening on http://localhost:8280

# Clientの起動
$ node client.js 
API1: OK
API2: OK
Completed.

2. Zipkinの構築

トレースデータを可視化するために、DockerコンテナのZipkinを構築します。

$ docker run -d -p 9411:9411 openzipkin/zipkin

ブラウザから localhost:9411 にアクセスすると、以下の画面が表示されます。

zipkin1.png

3. OpenTelemetryの組み込み

3.1 パッケージのインストール

それでは、1章で作成したサンプルにOpenTelemetryを組み込んでいきます。
まずは必要となるパッケージをプロジェクトにインストールします。

$ npm install \
  @opentelemetry/core \
  @opentelemetry/node \
  @opentelemetry/tracing \
  @opentelemetry/instrumentation \
  @opentelemetry/exporter-zipkin
  @opentelemetry/plugin-http \
  @opentelemetry/plugin-express

3.2 tracer.jsの作成

クライアント・サーバ両方が共通で使用するExporter設定(tracer.js)を作成します。

tracer.js
const opentelemetry = require('@opentelemetry/api');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/node');
const { SimpleSpanProcessor, ConsoleSpanExporter } = require('@opentelemetry/tracing');
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');

module.exports = (serviceName) => {
  const provider = new NodeTracerProvider();

  registerInstrumentations({
    tracerProvider: provider,
  });

  const exporter = new ZipkinExporter({ serviceName });
  provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
  provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
  provider.register();

  return opentelemetry.trace.getTracer('api-call-app');
};

Exporterとはトレースデータの出力先となるオブジェクトです。
今回は以下2つのExporterを設定し、2箇所に同時に出力されるようにしています。

使用するExporter 出力先
ZipkinExporter Zipkin
ConsoleSpanExporter コンソール画面

また、registerInstrumentations()を呼び出すことで、先ほどnpm installコマンドでインストールしたInstrumentationパッケージ(自動トレース出力)が使用されるようになります。
今回のサンプルでは、plugin-httpとplugin-expressの2つのInstrumentationが有効化されています。

3.3 クライアント側への組み込み

続いて、クライアント側ソースにOpenTelemetryのコードを追加します。

client.js
'use strict';

+const tracer = require('./tracer')('client');
+const api = require('@opentelemetry/api');
const axios = require('axios').default;

- async function setupRoutes() {
+ function makeRequest() {
+  const span = tracer.startSpan('client.makeRequest()', {
+    kind: api.SpanKind.CLIENT,
+  });

+  api.context.with(api.setSpan(api.ROOT_CONTEXT, span), async () => {

    // 1つ目のAPIを呼び出す
    await axios.get('http://localhost:8180/api/price')
    .then(res => span.setStatus({ code: api.SpanStatusCode.OK }))
    .catch(err => span.setStatus({ code: api.SpanStatusCode.ERROR, message: err.message }));

    // 2つ目のAPIを呼び出す
    await axios.get('http://localhost:8280/api/price')
    .then(res => span.setStatus({ code: api.SpanStatusCode.OK }))
    .catch(err => span.setStatus({ code: api.SpanStatusCode.ERROR, message: err.message }));
+  });

  // 5秒後に終了する
  setTimeout(() => { 
+    span.end();
    console.log('Completed.');
  }, 5000);
}

makeRequest();

クライアント側への追加内容は以下となります。

  1. Exporterの作成(tracer.js呼び出し)
  2. トレースデータを構成するSpanの作成(startSpan()の実行)
  3. ContextにSpanを設定し、API実行のタイミングでサーバ側へSpanを伝搬させる
  4. 最後にSpan.end()でSpanを終了する

用語説明になりますが、個々の測定区間をSpanと呼び、Spanの集合がTraceとなります。
以下の図のように、Spanは親子関係を持つこともできます。

trace_span.png

3.4 サーバ側への組み込み

api-server1.js
'use strict';

+const tracer = require('./tracer')('api-server1');
+const api = require('@opentelemetry/api');
const express = require('express');
const app = express();
const PORT = 8180;

const data = { name: "orange", price: 200};

async function setupRoutes() {
  app.use(express.json());

  app.get('/api/price', async (req, res) => {

    // 2秒後にレスポンスを返す
    setTimeout(() => {
      res.json(data);
    }, 2000);
  });
}

setupRoutes().then(() => {
  app.listen(PORT);
  console.log(`Listening on http://localhost:${PORT}`);
});

サーバ側への追加内容はExporterの設定のみです。
api-server2.jsにも同様の追加を行います(ソースは掲載省略)。

3-5. 動作確認

1章と同様に、サーバ2つを再度起動し、クライアントを起動します。

$ node client.js 

Zipkinの画面にアクセスし、検索条件:serviceName=clientを指定して、先ほど収集したトレースデータを表示します。
以下のような5段のトレースデータが表示されれば成功です。

zipkin2.png

それぞれのトレースデータは以下を表します。

収集箇所 説明 収集手段
1段目 クライアント startSpan()で作成したSpanの所要時間 手動
2段目 クライアント サーバ1に対するhttp getのリクエスト送信〜レスポンス受信までの所要時間 自動
3段目 サーバ1 リクエスト受信〜レスポンス送信までの所要時間 自動
4段目 クライアント サーバ2に対するhttp getのリクエスト送信〜レスポンス受信までの所要時間 自動
5段目 サーバ2 リクエスト受信〜レスポンス送信までの所要時間 自動

収集手段=自動は、Instrumentationが自動収集トレースデータです。

# 4. サーバ側の手動収集Spanの作成

オマケとして、サーバ側でも手動トレース収集を行ってみます。

api-server1
'use strict';

const tracer = require('./tracer')('api-server1');
const api = require('@opentelemetry/api');
const express = require('express');
const app = express();
const PORT = 8180;

const data = { name: "orange", price: 200};

async function setupRoutes() {
  app.use(express.json());

  app.get('/api/price', async (req, res) => {
+    const span = tracer.startSpan('processing', {
+      kind: api.SpanKind.SERVER
+    });

    setTimeout(() => {
+      span.end();
      res.json(data);
    }, 2000);
  });
}

setupRoutes().then(() => {
  app.listen(PORT);
  console.log(`Listening on http://localhost:${PORT}`);
});

サーバ側でstartSpan()すると、クライアントから伝搬されたトレースデータに新たなSpanが追加され、トレース測定が開始します。
測定をやめるタイミングで、span.end()します。

Zipkinで確認すると、processingという名前で手動収集したトレースデータが追加されているのがわかります。

zipkin3.png

おわりに

少ない手順で解析に有用なトレースデータを収集できるのはとても便利です。
ただ、使用するパッケージのバージョンによって手続きが異なっていて、少しでもズレた手続きをすると収集がうまくいかなくなるなど、成功パターンを編み出すのに結構時間がかかりました。
今後のバージョンアップで、もう少し使い方に自由度が出るといいなぁと願っています。

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

Passport.jsのローカル認証で特定のディレクトリ以下にアクセス制限をかける

はじめに

HTMLなどの静的ファイルを置いたディレクトリに対し登録済みユーザーのみアクセス可能なアクセス制限をかけようと、Node.js+Express.js+Passport.jsでWEBサイトを構築しました。
ローカル認証方式で特定のディレクトリだけでなく配下のすべてのディレクトリとファイルに対しアクセス制限を掛ける方法がネットでは見つけられずハマったので解決法を共有します。

ソース

Passport.jsの設定等は他のサイトをご参照下さい。
/app/restrict というディレクトリ以下すべてに対するGETメソッドについて認証を求める方法は以下の通りです。

app.get(/app\/restrict/ , (req, res, next) => {
    if (!req.isAuthenticated()) {
        req.session.originalUrlSave=req.originalUrl;
        res.render('login', { error: req.flash('error'), title: 'ログインページ' });
    }
    else {
        return next();
    }
});
  • ディレクトリの指定に正規表現を使います
  • 認証済みなら return next(); でGETしに来たファイルがそのまま返ります
  • restrictディレクトリ配下のURLに直接アクセスして来た場合に、ログイン画面でログイン成功したらそのURLに飛べるよう元のURLをセッションに保存しておきます。

終わりに

最初は

app.get('/app/restrict' , (req, res, next) => {

と書いていて、restrictディレクトリは意図通りアクセス制限が効くのですが、
/app/restrict/js
のようなサブディレクトリには全くアクセス制限が効いていないことに気づいて焦りました。
「指定したディレクトリの配下も掛かるハズ」と言った先入観を捨ててテストはしっかりやらないと怖いですね。

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

「オブジェクト名の変更」でアップロード済み画像のサムネイル生成を簡単に

クラウドファンクションでサムネイル生成

FirebaseのCloud Storageに画像をアップロードして、サムネイルをCloud Functionsで生成しているケースは多いと思います。
公式のサンプルでもCloud StorageのonFinalizeをトリガーにして、サムネイルを生成していますよね。
ただし、途中からサムネイルを生成するようになった、サムネイルのサイズを増やしたいなどのパターンでは、新規のクラウドファンクションを作成しても、すでにアップロードされた画像はonFinalizeのトリガーが発生せずに、サムネイルが生成されません。
そのため、他の方法を考える必要があります。

前提条件

今回この記事で参考になりそうな方はこのような条件です。

  • Cloud FunctionsのonFinalizeトリガーでサムネイルを生成している。
  • すでにアップロード済みの画像に対してサムネイル生成したい。
  • 簡単な実装でonFinalizeトリガーを発生させたい。
  • node.jsでスクリプトを書いて全ファイルに対して一括処理させたい。

ファイルをローカルにダウンロードしてアップロードしなおす(没案)

没案なので、読み飛ばしてもらって構いません。
当初は、画像をローカルの一時フォルダに格納し、それを同じ場所にアップロードし直せばonFinalizeトリガーが発生するので良いのではと考えていました。

没案となった実装内容

アップロード処理に関してはコミット履歴に残っていなかったのでTODOコメントとしました。
1行でかける処理だと思います。

// Storageから取得した画像パスに対する処理
imagePaths.forEach((imagePath) => {
    // 一時ディレクトリにdownloadディレクトリを作成する
    const downloadPath = os.tmpdir() + '/download/'

    // downloadディレクトリにStorageから取得した画像のパスを結合して、ローカル上の一時的なファイルパスとする
    const tempFilePath = downloadPath + imagePath;

    // storageから画像をローカルに保存する
    await bucket.file(imagePath).download({destination: tempFilePath});

    // TODO: アップロード処理
}

もちろんこれでも実現できますし、そんなに実装量も気にするほどではありませんが、デメリットも考えられます。

デメリット

  • 自分のPCのローカルストレージに一時的にファイルを置くため、ストレージ容量が足りないなどの問題が発生する可能性がある。
  • ダウンロードしてアップロードしなおす時のファイルパスが完全に一致するか検証しなければならない。

そして何よりもっとシンプルに書く方法はないかと辿り着いた結果が採用案になります。

オブジェクト名を変更する(採用案)

Cloud StorageのonFinalizeトリガーについて

Cloud StorageのonFinalizeトリガーは、バケットで新しいオブジェクト(または既存オブジェクトの新しい世代)が正常に作成された場合に送信されます。既存のオブジェクトのコピーまたは書き換えを行った場合にも送信されます。アップロードが失敗した場合、このイベントはトリガーされません。

Cloud Storageのトリガー

既存のオブジェクトの名前を変更

「既存のオブジェクトのコピーまたは書き換えを行った場合」がポイントです。
オブジェクト名の変更を行うことでonFinalizeトリガーが動くのではないかという予想が立ちました。
ただし、オブジェクト名は変更しますが、違う名前に書き換えてしまうと参照先のパスも変わってしまうので同じ名称に変更しなければなりません。
同じ名称にしたときに、オブジェクト名の変更として扱ってくれるかというのはポイントでしたが、問題ありませんでした。

オブジェクトの名前変更、コピー、移動についての公式ドキュメントはこちら

採用した実装内容

imagePaths.forEach((imagePath) => {
    await file.move(imagePath);
}

はい、すごくシンプルな実装にすることができました。
画像パスの面倒な結合処理などもなくなりましたし、ダウンロード => アップロードといった手順を踏まず、ファイルに対してmoveとすれば良いだけです。
ローカルのストレージを使用することもないので、容量を気にする必要もありません。
こちらを実行するとオブジェクト名の変更が行われ、onFinalizeが呼び出されました。
それによってアップロード済みの画像のサムネイルも生成することができました。

最後に

これに辿り着けたのは弊社内でペアプロを実施したのが大きかったです。
もともと前任の方のソースコードを引き継いでいて、アップロードからダウンロード処理するものだと思い込んでいたんですが、すごくシンプルな案にまとめることができました。

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

Node.js で Synology の WebDAV にアクセスする

ライブラリーのインストール

sudo npm install -g webdav

フォルダーの一覧

webdav_list.js
#! /usr/bin/node
//
//  webdav_list.js
//
//                      Mar/03/2021
// ---------------------------------------------------------------
const { createClient } = require("webdav")

const client = createClient(
    "https://example.synology.me:5006/",
    {
        username: "scott",
        password: "secret"
    }
)


// ---------------------------------------------------------------
async function webdevListfile (folder)
{
    const directoryItems = await client.getDirectoryContents(folder)

    console.log(directoryItems)
}

// ---------------------------------------------------------------
const folder = "/homes/scott/tmp"
webdevListfile(folder)
// ---------------------------------------------------------------

実行スクリプト

export NODE_PATH=/usr/lib/node_modules
#
./webdav_list.js

ファイルのアップロード

webdav_put.js
#! /usr/bin/node
//
//  webdav_put.js
//
//                      Mar/03/2021
// ---------------------------------------------------------------
var fs = require("fs")
// ---------------------------------------------------------------
const { createClient } = require("webdav");

const client = createClient(
    "https://example.synology.me:5006/",
    {
        username: "scott",
        password: "secret"
    }
);


// ---------------------------------------------------------------
async function webdevPutfile (file_in, file_out)
{
    const str = fs.readFileSync (file_in,'utf8')

    await client.putFileContents(file_out, str)
}

// ---------------------------------------------------------------
const file_in = "./tmp01.txt"
const file_out = "/homes/scott/tmp/tmp01.txt"

webdevPutfile(file_in,file_out)
// ---------------------------------------------------------------

実行スクリプト

export NODE_PATH=/usr/lib/node_modules
#
./webdav_put.js

ファイルのダウンロード

webdav_get.js
#! /usr/bin/node
//
//  webdav_get.js
//
//                      Mar/03/2021
// ---------------------------------------------------------------
var fs = require("fs")
// ---------------------------------------------------------------
const { createClient } = require("webdav");

const client = createClient(
    "https://example.synology.me:5006/",
    {
        username: "scott",
        password: "secret"
    }
);


// ---------------------------------------------------------------
async function webdevGetfile (file_in, file_out)
{
    const strx = await client.getFileContents(file_in, { format: "text" });

    fs.writeFile (file_out,strx, function (err)
        {
        if (err) {
            console.log("Error on write: " + err)
        } else {
            console.log("File written.")
            }
        })

}

// ---------------------------------------------------------------
const file_in = "/homes/scott/tmp/tmp02.txt"
const file_out = "./tmp02.txt"

webdevGetfile(file_in,file_out)
// ---------------------------------------------------------------

実行スクリプト

export NODE_PATH=/usr/lib/node_modules
#
./webdav_get.js
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1 MacへのWebAssembly環境構築手順

結論

良い点

  • WebAssembly(以下、Wasm)は実行環境を選ばない
  • 他言語との連携が可能
  • 5Gなど先の時代を見据えた高速な処理が可能
    • サーバーの処理をフロントで処理させることも可能
      • 動画変換など

イマイチな点

  • 資料が少ない
  • 環境が整っていない
  • 開発コストが高い(上記の理由により)

環境

  • macOS Big Sur ver11.2.1
  • zsh 5.8 (x86_64-apple-darwin20.0)

手順

Node.jsのインストール

過去のこちらの記事を参照しました

Rust のインストール

公式サイトまたは日本語サイトから手順を参照

特段理由が無い場合はrustupを用いてインストールを行うことが推奨されている

> curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

インストール方法を聞かれるがデフォルトで良い

You can uninstall at any time with rustup self uninstall and
these changes will be reverted.

Current installation options:


   default host triple: x86_64-apple-darwin
     default toolchain: stable (default)
               profile: default
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
> 1

下記、メッセージが表示されていればインストール完了

Rust is installed now. Great!

注意点として
パスは通してくれているが、現在のシェルに反映されてないので再読み込みさせる

To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH
environment variable. Next time you log in this will be done
automatically.

To configure your current shell, run:
source $HOME/.cargo/env
> source $HOME/.cargo/env

ライブラリをインストールする

Wasmへコンパイルするための標準ライブラリ

> rustup target add wasm32-unknown-unknown
info: downloading component 'rust-std' for 'wasm32-unknown-unknown'
info: installing component 'rust-std' for 'wasm32-unknown-unknown'

wasm-packのインストール

Wasmアプリケーションのbuildのために利用します

> cargo install wasm-pack

Installed package `wasm-pack v0.9.1` (executable `wasm-pack`)

以上です

余談

データ通信以外がすべてフロントエンドの技術で済む時代も来るのか!といった感じ
これからますますの発展に期待するばかりです

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

M1 MacへのNode.jsインストール手順

結論

以下の順でインストールします

Homebrew -> anyenv -> nodenv -> Node.js

環境

  • MacBook Air (M1,2020)
  • macOS Big Sur バージョン 11.2.1
  • zsh 5.8 (x86_64-apple-darwin20.0)
    • デフォルト状態でzshが利用されている想定

Homebrewのインストール

過去のこちらの記事を参照しました

anyenvのインストール

anyenvを利用する理由としては、今回利用するnodenv(Node.js)を始め、rbenv(Ruby)pyenv(Python)など他の言語もまとめて管理できるためです

# Homebrewからanyenvをインストールする
> brew install anyenv

# シェルにパスを通す
> echo 'eval "$(anyenv init -)"' >> ~/.zshrc $ exec $SHELL -l

# 現在のシェルへ反映させる
> exec $SHELL -l

nodenvのインストール

nodenv は Node.js のバージョン管理ツールです

# anyenvからnodenをインストールする
> anyenv install nodenv

# 現在のシェルへ反映させる
> exec $SHELL -l

Node.jsのインストール

(20210303時点)現在のM1 Macのデフォルト環境では14.*以前はインストールできません
※ Rossetaなどを利用してIntelベースに置き換えることができれば可能です

# インストール可能なバージョンを確認する
> nodenv install -l
(中略)
15.10.0

# バージョン名を指定してインストールする
> nodenv install 15.10.0

# インストールしたバージョンを反映させる
> nodenv global 15.10.0

余談

まだまだすべてがM1 Macに対応できているわけではないので注意が必要になります

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