GitHubのメールは開封チェックを行っていて、開封すると通知が既読になる

GitHubヘビーユーザーなら気づいたことはあると思うが、通知メールを開くをGitHub側の通知が既読になる。

これ、疑問に思ったことは無いだろうか。この記事ではどういう方法で開封チェックを行っているのかを見ようと思います。

TL; DR

  • imgタグで1x1 pixelのトラック機能つき画像を読み込むように設定されている。

Eメールの仕様

Eメールの仕様は昔から存在しており、HTTPの仕様も現在Webで使われているHTML Living Standardに沿っているわけではなく様々な実装があり混沌としている*1

これは、flexとか使いまくって書いたHTMLがうまくレンダリングされていなくてキレている id:cateiru

そのため、もともとJavascriptなどはかけるはずもなくそうなるとfetchなどもできない。

じゃあどうする

imgタグを使用しました。頭いい。

imgタグで画像を読み込むときにURLを叩きます。なので、そのURLにトラッキングIDを仕込んでおけば開封したかがわかるという仕組みです。

GitHub開封チェック

ここにあります。白色の1x1 pxのgif画像であるため見えませんが、Webインスペクタを参照することで見ることができます。

また、背景が黒の場合(スマホアプリなど)では直接画像を見ることができます。

この仕組を利用したサービス

mailtrack.io

mail trackなるものも存在します。全く同じ仕組みです。

nodeのargon2パッケージはAlpineで実行するとSIGSEGVエラーになる問題

7/20 追記

原因は、ビルド時と実行時のnodeのバージョンが違うことで発生する問題のようでした。

ビルド済みバイナリでは、node14でビルドしており実行は16の場合に問題を再現することができました。

解決策は、ビルド済みバイナリは使用せずにソースから直接ビルドする必要があります。

ref. [0.28.1] Node16 on Alpine suffers SIGSEGV error when using argon2 verify · Issue #302 · ranisalt/node-argon2 · GitHub

TL;DR

github.com

* 現在も修正されていません(というか、開発者も原因がわかっていないようです。私もわからなかったです。)

バグを見つけた背景

https://github.com/team-ful/noratomo を最近、大学のプロジェクトで作っていてそこでパスワードのハッシュ化でargon2を使用しています。

で、Cloud RunにデプロイしていてDockerfileのpurductionコンテナにnode:16-alpineを使っていました。

その状態で、アカウント作成やらログインやらを試したら500になってログを見るとコンテナが死んでいたので発見したという次第です。

このバグのやばいところ

  1. テストで見つからない。実装ミスとかじゃなくてOS依存でエラーが起きるため発見できなかった。
  2. 実行しないとエラーを見つけられない。Cのバイナリは事前インストールでも自前ビルドでも成功していた
  3. コンテナ自体が死ぬ。コンテナの再起動処理が走るので普通にやばい

解決策

今のところ2つしかありません。

  1. Alpine Linuxの使用をやめる

node:16にすると動きます。

  1. argon2のバージョンを0.27.2にダウングレードする

issueに書いてあるやつです。

このようなバグを事前に見つけるために

  • テストを本番環境と近い環境で実行する
    • 大事そう。もし実行に時間がかかるならGH Actionsとかを使用して非同期でテストを実行してもらえるともっと良さそう。

Cloud StorageのFake Serverをdocker-composeで実行する

Cloud Storage*1使っていますか?

私は最近、画像の保存場所によく使っています。

最近使ったものだと、大学のプロジェクトで作っているWebアプリのユーザアバターを保存する場所として使っています。

github.com

さて、このCloud Storageですがテストを書いたりデバッグをする中でローカルで実行させたいという話題がでます。

そのため、このブログではCloud Storageをdocker-composeで実行させる方法とかちょっと便利な使い方とかを紹介します。

fake-server

Cloud Storageは公式でローカルのDockerイメージは提供されていません。

そのため、fsouza/fake-gcs-serverを使用します。

github.com

version: '3'

services:
  gcs:
    image: fsouza/fake-gcs-server
    tty: true
    ports:
      - 4443:4443
    volumes:
      - ./.data:/data/[bucket name]
      - ./.storage:/storage
    command: -scheme http -public-host ${URL:-localhost}:4443

ここで、注意しておきたいのがvolumes/data/以下がbucket nameになる点です。ここで、bucket nameの空のディレクトリを作成しておかないとbucket nameを指定して接続した際にエラーが発生してしまいます。

また、fake-server実行時の-public-hostフラグはhttpで使用する際には必須となります。これは、その画像にアクセスできるホストを定義するもので通常、デバッグ用途などで使用するならlocalhostで十分です。しかし、SSHで作業している場合など特殊な場合を想定してURL環境変数が定義されている場合はそちらを使用するようにします。

docker-composeの環境変数.envに書くかexportするかで定義することができます*2

さらに、fake-serverで保存されているファイルは.storageにマウントすることでテストなどでちゃんと設定できているかも確認できます。

アプリケーションコード

これは、fake-serverが例を紹介しています。

github.com

おそらく、全てGoogle公式のGCSのパッケージを使用してアクセスできます。

Goとかはアクセスするのが一番簡単でSTORAGE_EMULATOR_HOST環境変数にfake-serverのホストを入れればそのままそこにアクセスしてもらえます。

// Set STORAGE_EMULATOR_HOST environment variable.
err := os.Setenv("STORAGE_EMULATOR_HOST", "localhost:9000")
if err != nil {
    // TODO: Handle error.
}

// Create client as usual.
client, err := storage.NewClient(ctx)
if err != nil {
    // TODO: Handle error.
}

// This request is now directed to http://localhost:9000/storage/v1/b
// instead of https://storage.googleapis.com/storage/v1/b
if err := client.Bucket("my-bucket").Create(ctx, projectID, nil); err != nil {
    // TODO: Handle error.
}

https://pkg.go.dev/cloud.google.com/go/storage#section-readme

Node.jsなんかは、そういった便利な環境変数は定義できないのでStorageオブジェクトのコンストラクタに引数で指定します。

// Creates a client
const storage = new Storage({
  apiEndpoint: "http://localhost:8080",
});

上記コードだとローカルしか実行できないので、以下のようにすると良さそうです。

let config = undefined;

if(process.env.NODE_ENV !== "production") {
  config = {
    apiEndpoint: "http://localhost:8080",
  };
}

const storage = new Storage(config);

公開する

GCP内の実行環境にデプロイすれば特に設定することもなく簡単にCloud Storageに接続できます。

// これで、何も設定しなくてもつながる(GCP内のサービスか、環境変数が設定されている場合)
const storage = new Storage();

また、別の環境でも同じことをしたい場合はGOOGLE_APPLICATION_CREDENTIALS環境変数にサービスアカウントキーの絶対パスを指定することでも可能*3です。

cloud.google.com

まとめ

GCSは便利なのでみんな使おう!!!

キーボード新調した

私のPCで使用しているキーボードは、6年ぐらい前に買ったものでした。

PCでゲームをよくするため、Logicool製の青軸ゲーミングキーボードです。

今、Amazonで調べたんですけどまだ売っているんですね。このキーボード、色が白でゲーミングによくあるカラフルにはできないのは欠点ですが音量調節ダイヤルがついていたりと色々使い勝手が良かったです。

買い替えた理由

主に3つあります。

1つ目は、このキーボードは青軸で結構音がうるさいことです。友達と通話しながらゲームをするときなどキーの音が入ってしまって結構ゲームに支障がありました。現在はDiscordがうまくフィルタしてくれているのでそういう問題はないと思いますが、家の壁が薄いので隣に響いてしまうぐらいには大きい音なのでいい加減新調しないとと思っていました。
(また、青軸は結構指が疲れるので長い時間ゲームをやっていると指が鍛えられるという問題?が起きていたのもあります。)

2つ目は結構使ってて寿命が来たことです。4年以上使っているのでDボタンのキーキャップが壊れていれ応急処置的にPgUPのキーとキャップを交換していてDボタンがPgUPになっていました。

最後はマイナポイントという不労所得が入ったことです。15000円貰えました!やったぜ

新しいキーボード

新しく買ったキーボードはRazerのBlackWidow V3 JP Yellowです。

青軸以外のゲーミングキーボード触るのってお店の展示以外はないので実際にゲームやるとすごく新鮮です。まじで指が疲れない!!!

また、このキーボード前のキーボードと同じく音量調節ダイヤルがついているのもポイント高いです。

新しいキーボードの欠点

1つあります。

黄軸はアクチュエーションポイントが1.2mm*1と短いのでキーに指を置いているだけで誤動作してしまうことが結構あります。多分慣れの問題なので時間が経てばなくなると思いますが勝手にピン刺されたりもの捨ててしまったりするので今は結構厄介な問題です。

まとめ

買ってよかった!!マウスもキーボードと同じぐらいの時期に買っているで早めに新調したい!!