おれの技術日記

元はJava+SQLがメインのエンジニア、フロントエンドは軽くかじった程度で苦手。最近忘れっぽいので覚えたことをいろいろメモするためにブログ開始。

GCPでオレオレGoogle Analytics(リアルタイム版)を開発する(その2)

前回の続き。次にCloud Functionsでブラウザから飛んできたビーコンを受ける口を作る。

1. Cloud Functionsを設定

f:id:kuniaki12:20200525164039p:plain
とりあえずエンドポイント名とかは何でもいいと思うけど、注意点は未認証の呼び出しを許可する必要があるくらいだろうか。これだけでAPIコールのエンドポイントができる。なんていい時代なのか。


あとはNode.jsのコードをポチポチ書くくらい。色々弄りながら最終的にこんなコードになった。

/**
 * Responds to any HTTP request.
 *
 * @param {!express:Request} req HTTP request context.
 * @param {!express:Response} res HTTP response context.
 */
exports.trackBeacon = (req, res) => {
  console.log(Date.now());

  var messageJson = {
    "ip" : req.ip
    ,"ua" : req.headers['user-agent']
    ,"uid" : req.query.uid
    ,"url" : decodeURIComponent(req.query.url)
    ,"timestamp" : Date.now()
  };

  var img = Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==", 'base64');
  res.writeHead(200, {
    'Content-Type': 'image/png',
    'Content-Length': img.length
  });
  res.end(img);

  const {PubSub} = require('@google-cloud/pubsub');
  const pubSubClient = new PubSub();

    async function publishMessage() {
      const topicName = 'projects/[プロジェクト名]/topics/[トピック名]';

      const dataBuffer = Buffer.from(JSON.stringify(messageJson));

      const messageId = await pubSubClient.topic(topicName).publish(dataBuffer);
      console.log(`Message ${messageId} published.`);
    }

    publishMessage().catch(console.error);

};

ブラウザから受け取って後続処理に流すのはIPアドレス・User Agent・Unique Identifier(uidパラメタ)・リクエスト発生元URL(urlパラメタ)・タイムスタンプ(エポックミリ秒)。また、画面には1x1の透過PNGを返す。その後にメッセージをPubSubに投げる。package.jsonはこんな感じ。Node.jsとか慣れてないので地味にここの書き方やらバージョン指定に苦戦。

{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
    "@google-cloud/pubsub": "1.7.3"
  }
}

ここに書いたバージョン番号はこのサイトで発見。
www.npmjs.com


2. 合わせてHTMLを変更

上記のパラメタに合わせてHTML側もこんな感じに変更。クッキーの扱いとかがやっつけなのは勘弁してください。

<!doctype html>
<html>
<body>
<h1>Hello World!</h1>
<script>
//UID生成
var N=48
var uid = getCookie('uid');
if(!uid){
	uid="";
	for(var i=0; i<N; i++){
		uid += S[Math.floor(Math.random()*S.length)];
	}
	document.cookie = "uid=" + uid + "; max-age=36000; secure; samesite=lax;";
}

//計測ビーコン
var imgUrl = '[Cloud Functionsのエンドポイント]';
var img = document.createElement('img');
img.src=imgUrl + '?uid=' + uid + "&url=" + encodeURIComponent(location.href);
document.body.appendChild(img);

function getCookie(key){
	var cookies = document.cookie;
	var cookiesArray = cookies.split(';');

	for(var c of cookiesArray){
		var cArray = c.split('=');
		if( cArray[0] == key){
			return decodeURIComponent(cArray[1]);
		}
	}
}
</script>
</body>
</html>