どこかのサービスに登録する時、本物のメールアドレスを渡すのがなんとなく嫌で、サービスごとに別のアドレスを使いたい、というのがきっかけです。
最初はエイリアスを発行して、届いたメールが見られれば十分だと思っていました。実際に触ってみると、どのアドレスに届いたのか、リンクを開いてよいのか、古いメールをいつ消すのか、まで見たくなってきます。気づいたら、発行するだけだった予定が受信箱になりかけています。
受信と管理画面を分ける
構成は、メールを受け取る Worker と、管理画面から操作する Worker を分けました。
受信側では、エイリアスを引いてメタデータを D1 に保存し、元の MIME は R2 に置きます。管理画面側は D1 の状態を見て、エイリアスやメッセージの一覧を出します。
メール
↓
受信 Worker
├─ D1: エイリアス・メッセージ・監査ログ
└─ R2: raw MIME
↓
管理 Worker / 管理画面
本文を全部データベースに詰めるより、検索や一覧に必要な情報と元データを分けた方が扱いやすい気がしました。この形にしておくと、後から全文検索を入れるか、raw MIME をそのまま残すか、という判断も変えやすそうです。
エイリアスの状態を増やす
エイリアスは作ったら終わりではありませんでした。
発行、停止、再開、アーカイブ、ローテーションを操作できるようにしました。最初は一覧の表示条件だけで考えていたのですが、停止したものを誤って再利用しない、アーカイブしたものを一覧から隠す、というのを状態遷移として考えると、受信時の判定や CSV 出力の条件も同じ基準で揃えられました。
状態が増えていく感じはありますが、これは地味に大事なやつだという気がしています。
受信箱として必要だったもの
メールを保存するだけでは、受信箱としては少し足りません。
未読、検索、サービス別の絞り込み、メッセージのリンク抽出、怪しそうな URL の警告を追加しました。通知もブラウザから出せるようにしました。
リンク抽出は便利そうですが、それ自体を安全判定にしてはいけない、と思っています。あくまで「このメールに URL が含まれている」と気づくための補助情報として表示することにしました。
後から、監査ログの画面遷移や受信箱のフィルターも見直しました。操作履歴が残っていると、なぜそのエイリアスが停止中なのかを追えます。
送信も必要になる
受け取ったメールを表示できると、そこから返信や転送もしたくなります。
送信元を設定可能にし、送信処理と通知処理を追加しました。ここでは、受信したメッセージをそのまま送信できると考えず、誰のエイリアスから、どの送信元を使って送るのかを別に確認する必要がありました。
受信側のアドレス管理と、外向きメールの送信者管理は似ているようで別の機能でした。ここを一緒にすると、たぶん後で混乱します。
モバイルと CSV で細かく詰まる
管理画面はデスクトップだけで見るものではなかったので、アカウント詳細画面をスマホ幅でも見直しました。メールアドレスをコピーする操作も、入力欄のように見えないと分かりにくい。
エイリアスの台帳を CSV に出す処理では、画面の一覧と同じルーティング条件を通っていない不具合がありました。表示側のデータが正しくても、出力側で別の集合を見ていました。ああ、これだ、という感じです。
一覧、CSV、保持削除のような「同じデータを使う処理」は、同じスコープと取得条件を共有した方がよさそうです。
保持期限を過ぎたメッセージを消す時は、先に D1 の記録を基準にし、対応する R2 の孤立オブジェクトも掃除するようにしました。ストレージだけ残るケースを放置すると、後で何が残っているのか分からなくなります。
おわり
メールエイリアスの発行だけなら小さい機能に見えますが、実際には受信箱、監査、送信、通知、保持削除がつながっていました。
個人用の道具なので、最初から大きなメールサービスにするつもりはありません。それでも、認証境界、データの所有範囲、状態遷移を曖昧にすると、すぐに管理しづらくなります。
次は、メッセージの検索をどこまで D1 で持つか、受信失敗や再送をどう見せるか。結局は「自分で見たい場所」が増えていくだけなのかもしれません。とりあえず今の形でしばらく使ってみて、足りないところを見つけていきます。