ハコヅミを作る #30:アカウントの画面と、データのエクスポート、退会を作る

前回(#29)は、運営の側の機能でした。今回は、利用者の側です。公開するなら、利用者が自分のアカウントを見て、データを持ち出して、やめられることが要ります。

  • アカウントの画面と、表示名(#140)
  • 自分のデータのエクスポート(#142)
  • 退会(#144)

アカウントの画面

アカウントの画面。上から順に、「アカウントの情報」(メールアドレス、会員区分「フリー」、登録した日)、「表示名」(入力欄に「たろキャン」と保存のボタン)、「使っている数」(ギア 4 / 300、消耗品 2 / 100、コンテナ 2 / 50、プラン 1 / 20、キャンプ 1 / 100、画像 2 / 300)、「データの書き出し」(説明と「データを書き出す」のボタン)、ログアウトのボタン、いちばん下に小さく「退会する」のリンクがある

アカウントの画面(データは架空)。エクスポートと退会の入り口も、ここに置く

出すのは、自分の情報だけです。メールアドレス、会員区分、登録した日、表示名、そして前回作った上限に対して、今いくつ使っているか、です。

表示名は、Google の名前と分ける

Google アカウントでログインすると、Google に登録した名前が手に入ります。多くの人は、本名です。

将来、キャンプの記録やおすすめのギアを、ほかの人に見せる機能(共有)を作るつもりです。そのとき、本名がそのまま出てしまうと困ります。そこで、表示名を、Google の名前とは別の列で持つことにしました。

決まりは、1つだけ強く書いています。表示名が未設定のとき、Google の名前を代わりに使わない、です。何もしなかった人の本名が、勝手に出ないようにするためです。

共有はまだ先なので、今は、アカウントの画面にしか出していません。それでも先に作ったのは、あとから「表示名を決めてください」と全員に頼むより、最初からあるほうが楽だからです。

テストが見つけた、使っていないデータ

「未設定のとき、Google の名前が使われない」ことを、E2E で確かめました。画面に名前が出ていないことに加えて、画面に渡しているデータの中にも入っていないことを見ます。

これが落ちました。Google の名前が、全部の画面に渡すデータの中に入っていたのです。

ログインを作ったとき(#27)に、ヘッダーの表示用として渡していたものでした。ヘッダーには出しておらず、データにだけ残っていました。画面に出さないものは、画面のデータにも入れない。外して、テストを通しました。

ヘッダーの右上を、入れ替える

広い幅のヘッダー。左にロゴとアプリ名、中央にキャンプ・ギア・消耗品・コンテナ・プランのメニュー、右に「管理」のリンクと、人の形のアイコンが付いた「アカウント」のリンクが並んでいる

右上は、ログアウトのボタンから、アカウントの画面へのリンクになった(「管理」は管理者にだけ出る)

ヘッダーの右上には、ログアウトのボタンがありました。これを、アカウントの画面へのリンクに置き換えています。ログアウトは、アカウントの画面の中に移しました。

ログアウトが1回遠くなりますが、セッションは30日あります。押す場面は、多くありません。スマホの狭いヘッダーに、ボタンを増やさずに済みました。


データのエクスポート

開発を始めたときから、「利用者のデータは、いつでも持ち出せるようにする」と決めていました。サービスをやめるときの約束にするためです。退会の前に、手元に残せるようにもしたい。そこで、退会より先に作りました。

「データを書き出す」を押すと、ZIP ファイルが1つダウンロードされます。

ファイル 中身
data.json 自分のデータの全部。機械で読める、欠けのない形
gears.csv など5つ ギア・消耗品・コンテナ・プラン・キャンプの一覧。人が読む用
images/ ギアとキャンプの画像

JSON だけでは、「持ち出せる」と言いにくい

最初は、JSON だけを考えていました。ただ、JSON は、ふつうの人には読めません。「持ち出せます」と言って渡すなら、Excel で開ける形も要ります。

CSV は、人が読むことだけを考えて作りました。

  • 見出しは日本語。「所有」「毎回用意する」など、状態や種類は、画面と同じ言葉にする
  • コンテナやプランは、id ではなく名前で出す
  • Excel で文字化けしないよう、先頭に BOM を付ける
  • プランの中身や、キャンプのチェックの状態は入れない(表が増えて、かえって分かりにくい。data.json には入っている)

ZIP は、ブラウザの中で作る

画像も入れるので、ZIP は数十 MB になることがあります。Cloudflare Workers は、使えるメモリが 128MB までです。サーバーで画像を全部集めて ZIP にすると、収まらないかもしれません。

そこで、ZIP は、ブラウザの中で作ることにしました。

  1. サーバーは、自分のデータを JSON で返すだけ
  2. 画像は、今ある画像の配信から、ブラウザが1枚ずつ取る
  3. ブラウザの中で、ZIP にまとめて、ダウンロードする

サーバーの側は、新しい処理が1つ増えただけです。画像を取っている間は、「画像を集めています 12 / 56」と、進み具合を出します。途中で読めない画像があっても、止めません。終わったあとに、読めなかった数を出します。

ZIP を作るために、fflate というパッケージを1つ足しました。依存を増やさない方針ですが、ZIP を自分で組み立てるコードを書いて保守するより、安全だと考えました。理由は PR に書いています。

入れないものを、決めておく

書き出したファイルは、人に渡すこともあります。入れないものを、先に決めました。

  • user_id、メールアドレス、Google の名前
  • セッション、Google のトークン
  • R2 のキー(画像の置き場所の名前)

画像のファイル名も、R2 のキーをそのまま使わず、ギアやキャンプの id から付け直しています。E2E では、書き出した ZIP を開いて、これらの文字がどこにも入っていないことを確かめました。

テーブルを足したときの、入れ忘れ

エクスポートで怖いのは、あとでテーブルを足したときに、書き出しに入れ忘れることです。気づかないまま、「全部のデータ」ではなくなります。

そこで、「利用者のデータのテーブルの一覧」を、1つのファイルに持つことにしました。そして、入れ忘れを2つの方法で見つけます。

  • テスト:DB の定義にあるテーブルが、すべて「利用者のデータ」か「そうでないもの」の一覧に入っているかを確かめる。新しいテーブルを足して、どちらにも入れないと、落ちる
  • 型:一覧にあるテーブルが、書き出すデータの型に入っていないと、型チェックでエラーになる

人が覚えておく決まりにせず、忘れたら落ちるようにしておけば、何年かあとの自分も困りません。


退会

退会の確認の画面。赤い文字で「退会すると、次のデータがすべて消えます。元に戻せません。」とあり、ギア 4、消耗品 2、コンテナ 2、プラン 1、キャンプ 1、画像 2 と件数が並ぶ。その下に、ログインの情報も消えることの説明と、「アカウントの画面の「データを書き出す」」へのリンクがある。下のカードには、「確認のため、「退会する」と入力してください」の入力欄と、「退会する」「やめる」のボタンがある

退会の確認の画面。消えるものの件数を出し、「退会する」と入力するまで、ボタンは押せない

すぐに消す。取り消しの期間は作らない

退会してから、しばらくデータを残しておき、取り消せるようにする作りもあります。ただ、そうすると、「消える予定」という状態と、定期的に消す仕組みが要ります。1人で保守するものが増えます。

ハコヅミでは、すぐに全部消すことにしました。そのぶん、間違えて消さないための手当てを厚くしています。

  • 消えるものの件数を、先に見せる
  • 「先にデータを書き出せます」と、エクスポートへのリンクを置く
  • 「退会する」と入力するまで、ボタンを押せない(サーバーでも確かめる)

消す順番は、R2 が先

消すものは、R2 の画像と、D1 の行です。この2つを、まとめて「全部成功するか、全部失敗するか」にはできません。

順番は、R2 を先、D1 をあとにしました。R2 の途中で失敗したら、D1 は消しません。D1 が残っていれば、ログインして、もう一度退会を押せます。

逆の順番だと、困ります。D1 を先に消して、R2 で失敗すると、ログインできないのに画像だけが残ります。誰の画像かをたどる手がかりも、消えています。

D1 のほうは、1回のバッチ(まとめて成功するか、まとめて失敗する)で消します。対象は、エクスポートで作った「利用者のデータのテーブルの一覧」の全部と、ログインの情報です。ここでも、一覧にテーブルを足して、消す処理に足し忘れると、型チェックでエラーになるようにしました。

退会できない人

2通りの人は、退会できないようにしています。

  • 管理者:自分を消すと、管理者がいなくなる。やめるときは、先に権限を外す
  • 凍結された人:前回決めたとおり、どの画面にも届かない。消してほしいときは、連絡をもらって、人が対応する

退会した人の記録は、残しません。同じ Google アカウントでもう一度ログインすると、データのない、新しいユーザーとして始まります。迷惑な使い方をした人が、退会して登録し直すことは、防げません。防ぐには、退会した人の記録を残すことになり、「データを残さない」約束とぶつかるためです。

受入確認は、もう1つのアカウントで

退会を、自分のアカウントで試すわけにはいきません。ここでも、もう1つの Google アカウントを使いました。データと画像を少し入れてから退会し、同じアカウントでログインし直して、空で始まることを確かめています。

本番では、退会は試していません。管理者の自分が退会の画面を開くと、「管理者は退会できません」と出ることだけを見ました。


本番に出す

ここまでの5つ(管理者、上限、アカウントの画面、エクスポート、退会)を、まとめて本番に出しました。

マイグレーションは3つで、どれも列やテーブルを足すだけです。今のデータは変わりません。リリースの直後は、自分も「一般・フリー」の扱いになります。本番の D1 のコンソールで、自分を管理者にする SQL を流すと、一覧の上限の表示が消えて、ヘッダーに「管理」が出ます。

公開までに残っているのは、次の4つです。

  • 公開の前の、セキュリティの見直し
  • ログインしていない人向けのトップページと、データが空のときの案内
  • 利用規約・プライバシーポリシー・お問い合わせ
  • Cloudflare Access を外す(ここで、一般に公開)

おわりに

  • 表示名は、ログインに使う名前と分ける。未設定のときに、本名を代わりに出さない
  • 画面に出さないものは、画面のデータにも入れない。テストで、データの中まで見る
  • 「持ち出せる」と言うなら、人が読める形(CSV)も用意する
  • 重い処理は、サーバーでやらずに、ブラウザに任せる手もある
  • 「テーブルを足したら、ここにも足す」は、決まりではなく、テストと型で守る
  • 消す順番は、「途中で失敗しても、やり直せるか」で決める
← トップページに戻る