• ..

AWS

    Express と JWT (JSON Web Token) をできるだけシンプルに使う

    依存

    まず必要なパッケージをインストールします。

    yarn add express express-jwt jsonwebtoken

    トークンを生成

    次の1行のコマンドで作れます。第2引数は復元に使うので覚えておきます。

    node -e "console.log( require('jsonwebtoken').sign({value:'foo'}, 'a') )"
    # eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YWx1ZSI6ImZvbyIsImlhdCI6MTUzNTU2NDUyNH0.Mkz75dMlWnK2Y_8g7CGjqwLNvlc1pC3O8znGuUP5ZS8

    復元を試します。第2引数は同じものを指定します。
    ちゃんとオブジェクトが表示されてれば大丈夫。

    node -e "console.log( require('jsonwebtoken').verify('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YWx1ZSI6ImZvbyIsImlhdCI6MTUzNTU2NDUyNH0.Mkz75dMlWnK2Y_8g7CGjqwLNvlc1pC3O8znGuUP5ZS8', 'a') )"                                                                         02:42:24
    # { value: 'foo', iat: 1535564524 }

    Expressサーバーで認証する

    まずlocalhost:3333okとだけ返す所まで作ります。

    const express = require('express');
    const jwt = require('express-jwt');
    const app = express();
    app.use('/', /* jwt middleware が入る */ (req, res) => res.sendStatus(200));
    app.listen(3333);

    app.useの第2引数でexpress-jwtを使います。設定は次の通り。

    • secretは上記で発行した時に使ったものを
    • requestPropertyは、デフォルトではreq.userに値が入るのですが、今回はユーザー情報でも何でもないのでなんとなくreq.data
      • ちなみにresultPropertyreq.res.dataになるみたいですなんか知った)
    • getTokenはトークンの置き場を自分で探して見つけたトークンを返してます。今はAuthorizationだけ見ます
    app.use(
      '/',
      jwt({
        secret: 'a',
        requestProperty: 'data',
        getToken(req) {
          if (
            req.headers.authorization &&
            req.headers.authorization.split(' ')[0] === 'Bearer'
          ) {
            return req.headers.authorization.split(' ')[1];
          }
    
          return null;
        }
      }),
      (req, res) => res.status(200).send(req.data)
    );

    実装は終わったで、あとはサーバーを建ててcurlで試してみます。レスポンスがjsonなら大丈夫です!

    curl -H 'Authorization: Bearer {トークン}' http://localhost:3333
    # {"value":"foo",...}

    UnauthorizedError: invalid signatureの場合はどこかおかしいのでsecret合ってるかどうかなど確認しましょう。

    Logger パッケージである Signale の使い方

    基本

    デフォルトタイプ

    最初から以下タイプが定義されてるので、足りてるならsignale[TYPE]('メッセージ')の形で即使うだけです。

    • await
    • complete
    • error
    • debug
    • fatal
    • fav
    • info
    • note
    • pause
    • pending
    • star
    • start
    • success
    • warn
    • watch
    • log

    以下はCLIでの使用例です。

    const signale = require('signale');
    
    const yargs = yargs.xxx(/* ... */);
    // 引数情報表示
    signale.info(`Port: ${argv.port}`);
    // サーバー起動処理
    signale.start('Server'));
    // App起動処理
    signale.start('App');
    // ℹ  info      Port:  33322
    // ▶  start     Server
    // ▶  start     App

    カスタムタイプ

    SignaleClass をインポートして、自分用に作る必要があります。

    const {Signale} = require('signale');
    
    const signale = new Signale({
      types: {
        sake: {
          color: 'green',
          badge: '🍶',
          label: 'sake'
        }
      }
    });
    signale.sake('Kokuryu');
    // 🍶  sake      Kokuryu

    補足情報をつける

    スコープ、

    const appLogger = signale.scope('cli:app');
    appLogger.start('listen');
    // [cli:app] › ▶  start     listen

    や現在のファイル名、

    signale.config({displayFilename: false});
    // [cli.js] › ✔  success   foo

    またdisplayDateで年月日、displayTimestampで時分秒を出したりできます。

    ログを表示しないようにする

    開発中だけ出したいとかあると思います。
    これは設定のstream/dev/nullに書き込むようにするとすべて捨てる事ができます。(以下はdev-nullパッケージを使ってます)

    const devnull = require('dev-null');
    const signale = new Signale({stream: devnull()});

    一部だけ消したい時は各typesで設定する必要があります。例えば、「debugタイプ捨ててぇ」時

    const devnull = require('dev-null');
    const signale = new Signale({
      types: {
        debug: {
          stream: [devnull()]
        }
      }
    });

    👦🏻「ログファイルに出してぇ」

    const signale = new Signale({
      stream: require('fs').createWriteStream('/tmp/foo.log')
    });

    GitHub API からあるリポジトリのファイル内容取得

    コード

    以下はとあるリポジトリのfoo.txtを取得しています。
    取得した内はbase64でエンコードされてるのでデコードが必要です。

    /**
     * `yarn add dotenv got`
     */
    
    require('dotenv').config();
    const got = require('got');
    
    (async () => {
      const {body} = await got(
        'https://api.github.com/repos/:owner/:repo/contents/foo.csv',
        {
          json: true,
          headers: {
            accept: 'application/vnd.github.v3+json',
            authorization: `token ${process.env.TOKEN}`
          }
        }
      );
    
      const decodedContent = Buffer.from(body.content, 'base64').toString();
      console.log(decodedContent);
    })().catch(err => {
      console.log(err);
    });

    .envにはTOKENを定義しておきます。

    TOKEN=xxx

    日本語ファイルを取得

    ファイルというか URL の日本語部分はパーセントエンコーディングします。テスト.csvを取得したいならこんな感じです。

    `https://api.github.com/repos/geekcojp/directory-maps/contents/${encodeURIComponent(
      'テスト'
    )}.csv`;

    過去の状態のファイルを取得

    URLの後ろにref={COMMIT_HASH || BRANCH || TAG}なParamをつけます。

    `https://api.github.com/repos/geekcojp/directory-maps/contents/${encodeURIComponent(
      'テスト'
    )}.csv?ref=fdbb18abe52965420c9c96b9502ddf14ba28dd7d`;

    Github から Personal Access Token の取得

    Settings -> 'Developer settings' -> 'Personal access tokens' から 'repository' にチェックを入れて生成

    npm install 時の nodejs バージョンに関するエラーの回避

    こういうやつ。

    # get-caller-file@2.0.1: The engine "node" is incompatible with this module. Expected version "6.* || 8.* || >= 10.*". Got "9.11.2"
    # get-caller-file@2.0.1: The engine "node" is incompatible with this module. Expected version "6.* || 8.* || >= 10.*". Got "9.11.2" error Found incompatible module

    Yarn を使っているなら --ignore-engines を付けると、ローカルのNodeJSのバージョンによってエラーになることを回避できます。

    yarn --ignore-engines

    サイトマップを作る

    スキーマ

    スキーマ情報はwww.sitemaps.org/protocol.htmlに書かれいます。手書きでxmlを書くのは面倒くさいのでnodejsスクリプトで生成する感じにしてみる。

    2つのパッケージをインストール

    以下の2つをインストールします。

    yarn add xml prettify-xml

    xmlは木構造なオブジェクトをxmlにフォーマットしてくれて、prettify-xmlはxmlの整形パッケージです。

    サンプルコード

    簡単に書いてみました。

    const fs = require('fs');
    const path = requrie('path');
    const xml = require('xml');
    const prettifyXml = require('prettify-xml');
    
    const tree = {
      urlset: [
        {_attr: {xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'}},
      ]
    };
    
    tree.urlset.push({
      url: [
        {loc: 'そのページのURL',
        {lastmod: 'いつ変更したか'},
        {changefreq: '更新頻度'},
        {priority: '優先度 0~1 例えば 0.5'},
      ]
    })
    // ...他すべてのページを`push`
    
    const sitemap = prettifyXml(xml(tree, {declaration: true}));
    fs.writeFileSync(path.join(__dirname, '相対パス/sitemap.xml'), sitemap);

    tree.urlset.push周りは色々リファクタして手間が少なくするようにするといいですね。
    ちなみにchangefreqは以下から適当なのを選びましょう。

    • always
    • hourly
    • daily
    • weekly
    • monthly
    • yearly
    • never

    lastmodchangefreqなどはは特に必須情報ではないようです。

    node コマンドでランダム色をさくっと取得

    node -e "console.log('#' + crypto.randomBytes(3).toString('hex'))"
    # #1f514c

    ランダム色をさくっと取得してcolorhexa.comで見る

    xargsと組み合わせればcolorhexa.comで詳細を見れるようにもできます。その場合色値の前の#はいらないので少し注意です。

    node -e "console.log(crypto.randomBytes(3).toString('hex'))" | xargs -I@ open https://www.colorhexa.com/@

    使っている node のバージョンが対象外で yarn 時にエラーになる

    僕が見たものだとこんなのや

    error get-caller-file@2.0.1: The engine "node" is incompatible with this module.
    Expected version "6.* || 8.* || >= 10.*".Got "9.11.2"

    こんなのです。

    error upath@1.0.4: The engine "node" is incompatible with this module.
    Expected version ">=4 <=9". Got "10.15.1"

    これは一応yarn--ignore-enginesというオプションをつければ回避できます。

    yarn --ignore-engines

    node-glob のパターンの書き方

    以下のようなファイル構造だとします。

    .
    ├── ba
    │   └── z.mdx
    ├── bar
    │   └── b.mdx
    ├── baz
    │   └── c.mdx
    └── foo
        └── a.mdx
    
    3 directories, 3 files

    0か複数

    *を使うと0か複数を表せます。

    glob('ba*/**/*.mdx', (_, files) => {
      console.log(files);
    });

    これは[ 'ba/z.mdx', 'bar/b.mdx', 'baz/c.mdx' ]を得ます。*の部分は無いものでもマッチするのでbaも含まれます。

    1か複数

    ?を使うと1か複数を表せます。

    glob('ba?/**/*.mdx', (_, files) => {
      console.log(files);
    });

    これは[ 'bar/b.mdx', 'baz/c.mdx' ]を得ます。?は必ず何かしらの文字列がある必要があるので今度はbaは含まれません。

    範囲

    []で囲むと範囲という意味になります。例えば[a-z]a,b,c,d,...みたいな意味になります。

    glob('qqqq/ba[a-z]/**/*.mdx', (_, files) => {
      console.log(files);
    });

    よってこれも[ 'bar/b.mdx', 'baz/c.mdx' ]を得ます。

    完全マッチの複数

    @(foo|b?r)と書くとfoob?rの時にマッチします。中で?*など使えるので柔軟に複数指定できます。

    glob('qqqq/@(foo|bar)/**/*.mdx', (_, files) => {
      console.log(files);
    });

    これは[ 'foo/a.mdx', 'bar/b.mdx' ]を得ます。

    複数マッチの否定

    !(foo|bar)と書くとfoobarじゃないものにマッチします。

    glob('qqqq/!(foo|bar)/**/*.mdx', (_, files) => {
      console.log(files);
    });

    これは[ 'qqqq/baz/c.mdx' ]を得ます。

    何でもいいディレクトリ

    **を使うとディレクトリの名前・どこにあるかに関係なくマッチするようにできます。

    glob('**/*.mdx', (_, files) => {
      console.log(files);
    });

    これは[ 'foo/a.mdx', 'bar/b.mdx', 'baz/c.mdx' ]を得ます。これはhoge/fuga/piyo/a.mdxというようなファイルであってもマッチします。

    エラー色々

    Electron ビルド時

    環境

    • MacOS Mojave

    ログ

    Unhandled rejection Error: Could not find "wine" on your system.
    
    Wine is required to use the appCopyright, appVersion, buildVersion, icon, and
    win32metadata parameters for Windows targets.
    
    Make sure that the "wine" executable is in your PATH.
    
    See https://github.com/electron-userland/electron-packager#building-windows-apps-from-non-windows-platforms for details.
        at Process.ChildProcess._handle.onexit (internal/child_process.js:232:19)
        at onErrorNT (internal/child_process.js:407:16)

    直す

    文にあるように、以下の手順でwineをインストールします。

    # Homebrew をインストール
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    
    brew cask install xquartz
    # ...
    # ==> Downloading https://dl.bintray.com/xquartz/downloads/XQuartz-2.7.11.dmg
    # ...
    # 🍺  xquartz was successfully installed!
    
    brew install wine
    # ...
    # ==> Downloading https://homebrew.bintray.com/bottles/wine-3.0.4.sierra.bottle.tar.gz
    # ...
    # ==> Caveats
    # You may also want winetricks:
    #   brew install winetricks
    # ==> Summary
    # 🍺  /usr/local/Cellar/wine/3.0.4: 8,704 files, 633.5MB

    以下のようにダイアログが建つかもなので、インストールしてください。

    NODE_MODULE_VERSION 57. This version of Node.js requires NODE_MODULE_VERSION 59. Please try re-compiling or re-installing

    rm -rf node_modulesして削除後、再度yarnでインストール