- 投稿日:2020-02-28T23:06:02+09:00
nuxtでDOMException: Failed to execute 'appendChild' on 'Node'エラー
- 投稿日:2020-02-28T18:38:32+09:00
amazon-qldb-driver-nodejsからQLDBを使う①(接続編)
QLDBとは
https://aws.amazon.com/jp/qldb/
Amazonが提供するフルマネージド型の台帳データベースです。
ブロックチェーンとは異なり中央集権で管理されます。データに対する変更は全て記録され、後から確認可能なようです。
また、変更履歴が正確であることを暗号的に検証する機能を提供しています。
https://docs.aws.amazon.com/qldb/latest/developerguide/verification.html中央集権からトランザクションの実行時にネットワーク参加者の合意を経る必要がないため、一般的なブロックチェーンベースのフレームワークより高いスループットが出るようです。
以上から、「データの信頼性やトレーサビリティを担保したいけど、分散型である必要はない」などの場合にとても魅力的な選択肢になりそうです。
amazon-qldb-driver-nodejsについて
プログラムからアクセスする場合は現状はJavaのdriverを使うのが主流なようです。
nodejs用のdriverも用意されており、今回はこちらを使ってQLDBにプログラムから接続してみます!
現在はまだpreviewで本番環境用途に使用するのは推奨していないようですので、ご注意下さい。
- https://docs.aws.amazon.com/ja_jp/qldb/latest/developerguide/getting-started.nodejs.html
- https://github.com/awslabs/amazon-qldb-driver-nodejs
前提
今回のサンプルはAWSのコンソールで台帳およびいくつかテーブルを作成してある状態を前提とします。
筆者の環境では以下のチュートリアルで登録したデータ使って動作確認しました。
https://docs.aws.amazon.com/ja_jp/qldb/latest/developerguide/getting-started.htmlチュートリアルは以下の記事が参考になりました。
https://qiita.com/yanyansk/items/586b7f1c86eca4352b44IAMユーザのアクセスキーの作成
実装する前にQLDBにアクセスするための認証情報を作成する必要があります。
以下の手順でアクセスキーを発行して下さい。サービス > IAM > ユーザ からユーザの追加を選択して下さい。
任意のユーザ名を入力して下さい。アクセスの種類は「プログラムによるアクセス」にチェックを入れて下さい。
一旦はテストで使用するだけなので、ユーザグループなどは作成せず、ポリシーを直接アタッチします。
タグなどは特に必要がないので、ユーザーの作成まで完了して下さい。
作成完了の画面で表示される「アクセスキーID」と「シークレットアクセスキー」を控えて下さい。
driverからQLDBにアクセスする際にこの情報を使って認証します。実装
driverや必要なモジュールのinstall
npm i amazon-qldb-driver-nodejs aws-sdk ion-js typescriptcredential情報の編集
「credentials.json」などの名前で以下のファイルを作成してください。
{ "accessKeyId": "${作成したアクセスキーID}", "secretAccessKey": "${作成したシークレットアクセスキー}" }認証部分の実装
認証情報の設定
作成したcredentialのjsonを使って認証します。
const AWS = require("aws-sdk"); AWS.config.loadFromPath("./credentials.json");Credentialの確認
デバッグしやすいように、Credentialが正しく設定されているか確認するfunctionを追加します。
function checkCredential() { return new Promise((resolve, reject) => { AWS.config.getCredentials(function (err: Error) { if (err) { return reject(err); } console.log("Access key:", AWS.config.credentials.accessKeyId); console.log("Secret access key:", AWS.config.credentials.secretAccessKey); resolve(); }); }) }メインフローの実装
メインの部分を実装していきます。
セッションの作成
regionはQLDBを作成したリージョンを設定してください。
PooledQldbDriverの第一引数には作成した台帳の名前を指定して下さい。const testServiceConfigOptions = { region: "{QLDBを作成したリージョン}" }; const qldbDriver: PooledQldbDriver = new PooledQldbDriver("{作成した台帳の名前}", testServiceConfigOptions); const qldbSession: QldbSession = await qldbDriver.getSession();台帳上のテーブルの確認
for (const table of await qldbSession.getTableNames()) { console.log(table); }動作確認
実装したコードの全文は以下になります。
こちらを動作確認してみます。import { PooledQldbDriver, QldbSession } from "amazon-qldb-driver-nodejs"; const AWS = require("aws-sdk"); AWS.config.loadFromPath("./credentials.json"); (async () => { await checkCredential(); const testServiceConfigOptions = { region: "{QLDBを作成したリージョン}" }; const qldbDriver: PooledQldbDriver = new PooledQldbDriver("{作成した台帳の名前}", testServiceConfigOptions); const qldbSession: QldbSession = await qldbDriver.getSession(); for (const table of await qldbSession.getTableNames()) { console.log(table); } })().catch(err => { console.error(err); }); function checkCredential() { return new Promise((resolve, reject) => { AWS.config.getCredentials(function (err: Error) { if (err) { return reject(err); } console.log("Access key:", AWS.config.credentials.accessKeyId); console.log("Secret access key:", AWS.config.credentials.secretAccessKey); resolve(); }); }) }$ npx tsc main.ts $ node main.js Access key: xxxxxxxxxxxxxxxxxx Secret access key: xxxxxxxxxxxxxxxxxx VehicleRegistration DriversLicense Vehicle Person無事台帳上のテーブル名が表示されました!
次回は検索やデータの登録について書きたいと思います!さいごに
ZEROBILLBANKでは一緒に働く仲間を募集中です。
ZEROBILLBANK JAPAN Inc.
- 投稿日:2020-02-28T15:27:04+09:00
LambdaでCognito認証(ユーザー認可)
はじめに
SDKをローカルに持ってきてゴニョるサンプルは検索に引っかかるのですが、
クラウド側(Lambda関数内部)で完結するサンプルが見つからない...
よし、ならば投稿してしまえ。トップ
├ユーザー作成
├ユーザー確認
├ユーザー認証
└ユーザー認可 ←イマココ注意事項
本稿は、こちらの記事をLambda関数で実現することを目的としています。
兄弟記事とは毛色が違いますので、予めご了承ください。
- Decode and verify Amazon Cognito JWT tokens (Amazon Cognito JWT トークンを復号して検証する)を、JavaScriptで改変したプログラムを使用します。
- npmを使用してライブラリを入手し、Lambdaにアップロードする手順が含まれます。
- Lambda関数でAPI Gateway用のオーソライザーの作成方法を解説する記事ではありません。
- API Gatewayでオーソライザーの設定方法を解説する記事でもありません。→こちらをご覧ください。
ユーザー認可 (Authorization)
通常、ユーザー認可はAPI Gatewayのオーソライザーに任せればいいのですが、
諸事情によりAPI Gatewayに任せられない場合は、Lambdaに担当させる必要があります。
本稿では、Lambda関数でユーザー認可する方法を解説します。ドキュメント
AWSJavaScriptSDKは使用しません。
JSON ウェブトークンの検証npm
ユーザー認可に使用する外部ライブラリを入手します。
外部ライブラリ
npmで入手します。
入手した外部ライブラリはLambdaにアップロードします。npm i jwk-to-pem --save npm i jsonwebtoken --saveCognito
Cognitoのユーザープールから、ユーザー認可に使用する「公開キー」と「アプリクライアントID」を入手します。
公開キー
在り処・・・https://cognito-idp.{リージョン}.amazonaws.com/{プールID}/.well-known/jwks.json
※プールIDはCognitoのユーザープールの「全般設定」のページから確認できます。
アプリクライアントID
在り処・・・Cognitoのユーザープールの「全般設定>アプリクライアント」のページから確認できます。
※アプリクライアント未作成の場合は作成してください。Lambda
関数を一から作成します。
環境設定
ソースコード
↓適当な例外をthrowしています。
アプリクライアントIDや発行者(issuer)の文字列は、Lambdaの環境変数に設定すると、プログラムの見通しが良くなると思います。authorizer.js'use strict'; // ライブラリ const jwkToPem = require('./node_modules/jwk-to-pem/src/jwk-to-pem.js'); const jsonwebtoken = require('./node_modules/jsonwebtoken'); // 公開キー。 https://cognito-idp.{リージョン}.amazonaws.com/{プールID}/.well-known/jwks.json と同じもの const jwks = require('./jwks.json'); // JWT形式判定用の正規表現 const jwtRe = /(?<header>.+)\.(?<payload>.+)\.(?<signature>.+)/; /** * オーソライザー (トークンを検証する) * @param {string} token IDトークン * @returns {Object} ペイロード */ module.exports = token => { // 入力はJWT形式の文字列か? const match = jwtRe.exec(token); if (!match) throw 'AuthError'; // ヘッダー情報を取得 const header = JSON.parse(Buffer.from(match.groups.header, 'base64').toString()); // 公開キーから使用するキーを選ぶ const jwk = jwks.keys.find(k => k.kid == header.kid); if (!jwk) throw 'AuthError'; // 外部ライブラリを使って署名を確認する const pem = jwkToPem(jwk); const claim = jsonwebtoken.verify(token, pem); // 有効期限の確認 const now = new Date() / 1000; if (now > claim.exp || now < claim.auth_time) throw 'AuthError'; // 利用者(audience)の確認 if (claim.aud !== '{アプリクライアントID}') throw 'AuthError'; // 発行者(issuer)の確認 if (claim.iss !== 'https://cognito-idp.{リージョン}.amazonaws.com/{プールID}') throw 'AuthError'; // トークン種別の確認 // IDトークンを使う場合の claim.token_use は 'id' // アクセストークンを使う場合の claim.token_use は 'access' if (claim.token_use !== 'id') throw 'AuthError'; // ハレて認証が通った return claim; };↓例外をcatchしていませんが、そのあたりを含めて適当に改変してください。
index.js'use strict'; // オーソライザー const authorizer = require('./authorizer.js'); /** * メイン * @param {Object} event プロキシ統合の情報 * @returns {Promise<HTTPResponse>} HTTPレスポンス */ exports.handler = async event => { // event情報からIDトークンを取得 const token = event.headers['Authorization']; // HTTPヘッダーのAuthorizationにIDトークンがある場合の例 // IDトークンをオーソライザーに投入 const claim = authorizer(token); // 例外が発生しなければ認可成功 // HTTPレスポンスを返して終了 return { statusCode: 200, body: 'Authorization succeeded.', }; };
- 投稿日:2020-02-28T12:21:42+09:00
docker コマンドだけで起動する proxy サーバ
proxy という npm パッケージを node コンテナで実行するだけでお手軽に proxy サーバが起動できる。
コマンド
$ docker run --rm -p 8888:3128 node npx proxy
- proxy コマンドはデフォルトで
3128
ポートをListenするのでそれをホスト側の8888
にポートマッピングしているブラウザの設定(Firefoxの場合)
- Manual Proxy setting でホスト名を
localhost
、ポート番号を先のコマンドでポートマッピングしたportに設定参考
- 投稿日:2020-02-28T11:05:44+09:00
Auth0のログをリアルタイムでエクスポートできるようになったぞー
Auth0のログをBigQueryを使って永続化したかった - Qiita
こちらの記事でも書かせていただいたとおり、 Auth0 のログ保有期間 Enterprise でも30日と、比較的保有期間が短いことがネックになっていました。私が今担当しているプロジェクトでは Firebase を利用しているため、
ログを定期的に API を使って取得し、 Storage にエクスポートしています。ふとダッシュボードを見ると、Logs の中に Streams という機能が追加されており、見ると AWS の EventBridge と Webhook が使えそうなので、早速使ってみました。
Log Streams
Log Streams - Auth0
HTTP Event Log Streams - Auth0このドキュメントを読むとおり、イベントが起きたタイミングで指定の Webhook をコールしてくれるので、以前書いた記事のような
onRun()
ではなく、使い慣れたonRequest()
が利用できるようになります。ログの内容は body に入ってくる仕様です。以前の記事にも書きましたが、 Stackdriver 経由で Storage にエクスポートしているので、今回は Stackdriver にぶち込む処理をこんな感じで書いてみました。
utils/logToStackDriverLogging.tsimport { Logging } from '@google-cloud/logging'; interface firebaseEnvConfig { databaseURL: string; storageBucket: string; projectId: string; } const firebaseConfigRaw = process.env.FIREBASE_CONFIG as string; const firebaseConfig: firebaseEnvConfig = JSON.parse(firebaseConfigRaw); const logToStackDriverLogging = async (payload: any) => { const logging = new Logging({ projectId: firebaseConfig.gcloudProject, }); // NOTE: この辺はお好みで const log = logging.log('auth0'); const metadata = { resource: { type: 'global', }, }; const entry = log.entry(metadata, payload); await log.write(entry); }; export default logToStackDriverLogging;src/logStream.tsimport * as functions from 'firebase-functions'; import * as express from 'express'; import * as bodyParser from 'body-parser'; import logToStackdriverLogging from '../utils/logToStackDriverLogging'; const app = express(); app.use(bodyParser.json()); app.post('/auth0', async (req, res) => { res.setHeader('Content-Type', 'text/plain'); const authorization = req.get('Authorization'); if (!authorization) { console.error('No authorization header'); res.status(400).json({ success: false }); return; } // NOTE: ここはよしなに。 if (authorization !== functions.config().auth0.token) { console.error('Authorization Token missmatch'); res.status(401).json({ success: false }); } const { body } = req; await logToStackdriverLogging(JSON.stringify(body)); res.status(201).json({ success: true }); }); export const logStream = functions.https.onRequest(app);Cloud Functions にデプロイしたら、あとは Auth0 のダッシュボードから、 Logs → Streams とたどり、 Custom Webhook の設定をします。
こんな感じにて Save 。
あとはログインしたりログアウトしたりします。いけましたね あとは Stackdriver → Storage のエクスポートは既に設定済みなので今回は説明省略しますが、ちゃんとエクスポートされていればOKです。
注意点
受け取る側の Webhook がエラーになった場合、3回は Auth0 はイベントを送ってきます。こちら側でrerunする処理を考えなくていいのでとても親切ですね!逆に言うと、エラーを吐いちゃうエンドポイントを何度も叩くことになりかねないので注意してください。
If your server is unable to receive the event, we will retry up to three times to deliver the event
それと、「リアルタイムエクスポート」って書きましたが、嘘です。たまに遅延します。多分これはどのサービスでも発生する可能性がありますが、ベストエフォートです。
Auth0 does not provide real-time logs for your tenant. While we do our best to index events as they arrive, you may see some delays.
それでは!
- 投稿日:2020-02-28T09:27:48+09:00
花粉症対策デジタル医療相談Botの開発 GASでユーザー毎に飛散予測を定時プッシュ
概要
現在スギ花粉症患者さん向けに医療機関を受診しなくてもLINEで市販の医療用医薬品を使い分けることが出来るサービスを開発中です。
3月4日までCAMPFIREでテスト版ユーザー募集中です。花粉症の方は是非ご参加ください!
CAMPFIREのプロジェクトページはこちら
LINEで花粉症の重症度や最適な市販薬がわかるデジタル医療相談【アレルナビ】以前のQiita記事はこちら
花粉症対策デジタル医療相談Botの開発 ユーザーIDと位置情報をFirestoreで管理ユーザーが特定した地点のピンポイント花粉飛散予測を定時にプッシュする機能を開発中なのでその辺りをまとめました。
作成方法
1. 気象予測API
今回のアプリではこちらの気象APIを利用しています。
Lifesocket2. プログラムを作成
気象データを取得する関数 getweather()async function getweather(userlat,userlong) { const BASE_URL = "***************************************"; let PATH = `/location/${userlat}/${userlong}`; //緯度経度 let url = BASE_URL + PATH + "?days=7"; //7日分取得の場合 let res = await axios.get(url, { headers: { "Content-Type": "application/json", Accept: "application/json", "x-access-key": "***********************************" } }); return res.data; }飛散予測をプッシュする関数pushpollen()
async function pushpollen() { //Firebaseから位置情報データを受ける let locationsRef = db.collection('locations'); let alllocations = locationsRef.get() .then(snapshot => { snapshot.forEach(async doc => { console.log(doc.id, '=>', doc.data()); let lineId = doc.data().line_user_id; let userlat = doc.data().latitude; let userlong = doc.data().longitude; if (lineId == null) { console.log("IDなし") } else { let item2 = await getweather(userlat,userlong); console.log(item2); let url2; let [date, time] = item2.Daily[1].DateTime.split("T"); if (item2.Daily[1].Index === 0) { url2 ="**********"; } else if (item2.Daily[1].Index === 1) { url2 ="**********"; } else if (item2.Daily[1].Index === 2) { url2 ="**********"; } else if (item2.Daily[1].Index === 3) { url2 ="**********"; } else if (item2.Daily[1].Index === 4) { url2 ="**********"; } client.pushMessage(lineId, { type: "template", altText: "This is a buttons template", template: { type: "buttons", thumbnailImageUrl: url2, imageAspectRatio: "rectangle", imageSize: "cover", imageBackgroundColor: "#FFFFFF", title: `${date}${item2.PinpointName}の花粉飛散情報`, text: item2.Daily[1].IndexCommentary, defaultAction: { type: "uri", label: "View detail", uri: "**********" }, actions: [ { type: "message", label: "花粉症の重症度を判定", text: "判定" } ] } }) } }); }) .catch(err => { console.log('Error getting documents', err); }); }3. GASで定時実行
1.GASファイルを作成
Googleにアクセスしてログイン
Google App Script
新しいプロジェクトを選択
2.コード.jsに以下のように書く
function myFunction() { UrlFetchApp.fetch("実行させたいURL/cron"); }3.トリガーを追加
コード編集画面のメニューにある時計マークをクリック
pushしたい時間帯に設定
(何分おきや何時間おき、毎週何曜日のこの時間なども設定できます)
4. プログラムの書き換え
以下を追記して定時に飛散予測をプッシュする関数pushpollen()が実行されるようにする。app.get("/cron", (req, res) => { pushpollen(); });完成図
考察
非同期処理の理解が浅かったためユーザーID毎に異なる飛散情報をプッシュさせるまでにかなり苦労しました。定時実行に関してはGASを使うと無料で簡単にCronのように時間指定で任意のURLをリクエストできちゃうのでとても便利ですね。
- 投稿日:2020-02-28T08:51:44+09:00
AWS Lambda入門(Node編)
概要
- ServerlessFrameworkを使ってLambda関数を作り、ローカルで動作確認したあとにAWSにデプロイしてアクセスするところまでやってみます
Lambdaとは
- LambdaはAWSが提供するサービスの1つで以下のような特徴を持ちます
サーバーレス
- 通常のアプリケーションはサーバにデプロイし稼働させることでアクセスすることができますが、当然サーバが止まっていたら利用することはできません
- Lambdaはサーバーレスに分類されるサービスで、アクセスがあるとそのつど起動し処理が実行され終了すると停止します
- つまりLambdaはサーバの死活監視のようなことをする必要がなく、また課金も実行時間単位なので金銭面でもお得といった特徴があります
FaaS
- LambdaはいわゆるFaaS(Function as a Service)に分類されます
- つまり、Function(関数)をデプロイして、それを公開するサービスというわけですね
関数の作成
- 今回はServerlessFrameworkを使います
ServerlessFrameworkのインストール
- グローバルにインストール
npm i -g serverless
- 動作確認
sls -v
ServerlessFrameworkのコマンドが
sls
とserverless
のどちらでも動きます
- 以下のような内容が表示されればOKです
Framework Core: 1.64.1 Plugin: 3.4.1 SDK: 2.3.0 Components Core: 1.1.2 Components CLI: 1.4.0雛形の生成
- ServerlessFrameworkの機能でLambda用の関数や設定ファイルの雛形を生成します
- 今回は
aws-nodejs
というtemplateを指定します- 他にどんなtemplateがあるかは
serverless create --help
を実行すると見ることができますmkdir sls-sample cd sls-sample serverless create --template aws-nodejs
- 以下のようなファイルが生成されているはずです
% tree -a . ├── .gitignore ├── handler.js └── serverless.yml
handler.js
は今回のメインのファイルでLambdaで実行する処理を書きますserverless.yml
はServerlessFrameworkを使う上での設定ファイルです.gitignore
はgit管理する際にServerlessFrameworkが生成する一時ファイルを管理対象外にするための記載が追加されています関数をローカルで実行してみる
- AWSにデプロイする前にまずはローカルで動作確認します
関数の内容を確認
- 実行する前に
handler.js
の中身を確認しましょう'use strict'; module.exports.hello = async event => { return { statusCode: 200, body: JSON.stringify( { message: 'Go Serverless v1.0! Your function executed successfully!', input: event, }, null, 2 ), }; // Use this code if you don't use the http event with the LAMBDA-PROXY integration // return { message: 'Go Serverless v1.0! Your function executed successfully!', event }; };
- メインの処理である関数を
hello
という名前でmodule.exports
によって外部からアクセス可能にしています- 関数の中を見てみると
return
文しかありませんstatusCode
とbody
の2つのプロパティを持ったobjectを返却しています
statusCode
は実行結果を表現していて200
は成功を意味しています(詳しくはググって)body
は実行結果のメイン部分でメッセージを定義したmessage
と入力値をそのまま返却するinput
を返していますローカルで実行する
- それでは実行してみましょう
- ServerlessFrameworkを使うとAWSにデプロイせずともLocalマシン上で動作確認ができます
sls invoke local --function hello
sls invoke
が関数を呼び出すためのコマンドです
- そのあとの
local
はAWSにアクセスするのではなく手元のファイルにアクセスすることを意味しています- 最後の
--function hello
は実行したい関数を指定していています
- 先程
hello
という名前でexportしていることを確認しましたね- 実行結果はこんな感じです
{ "statusCode": 200, "body": "{\n \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n \"input\": \"\"\n}" }
input
が空っぽなので適当な値を渡してみます
--data
で値を渡すことができますsls invoke local --function hello --data Hello{ "statusCode": 200, "body": "{\n \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n \"input\": \"Hello\"\n}" }
- Helloも取得できることが確認できました
関数をAWSにデプロイして実行してみる
- AWSにデプロイしてアクセスしてみます
AWSにアクセスするための設定
- AWSにデプロイするためにはキー情報の設定が必要になります
- アクセスキーの発行についてはIAM ユーザーのアクセスキーの管理を参考に実施してください
- 以下のコマンドの
aws_access_key_id
にAccess key IDを、aws_secret_access_key
にSecret access keyを入れて実行してください
~/.aws/credentials
がすでに作成されている場合は上書きされるか尋ねられるので問題ないかファイルの内容を確認し対応してくださいserverless config credentials --provider aws --key aws_access_key_id --secret aws_secret_access_key
- キー情報が漏洩し悪用されると多額の請求につながる危険性があるので取り扱いには十分気をつけてください
デプロイする
- デプロイもServerlessFrameworkの機能で簡単に実行できます
serverless deploy --region ap-northeast-1
--region ap-northeast-1
はAWSの東京リージョンにデプロイすることを指定しています
serverless.yml
に記載しておけば毎回引数で設定する必要はなくなりますserverless.yml# 抜粋 provider: name: aws runtime: nodejs12.x region: ap-northeast-1 # これ
- デプロイが成功すると以下のような出力がされます
Service Information service: sls-sample stage: dev region: ap-northeast-1 stack: sls-sample-dev resources: 6 api keys: None endpoints: None functions: hello: sls-sample-dev-hello layers: None Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
- エラーが出た場合は出力されたログをよく確認して対処しましょう
デプロイした関数を実行する
- AWSにデプロイしたLambda関数もコマンドラインから実行することができます
sls invoke --function hello --data Hello --region ap-northeast-1
- ローカルで実行したときとの違いは
local
を指定していないだけですね- ローカルのときと同じように以下のレスポンスを受け取れていれば成功です
{ "statusCode": 200, "body": "{\n \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n \"input\": \"Hello\"\n}" }まとめ
- Lambda関数の基本的な扱い方について紹介しました
- ServerlessFrameworkを使うと設定やデプロイ周りがとても簡単ですね!