アクセス制御¶
制御は 2 段である。 手前が Cloudflare Access(誰がアプリに入れるか)、奥が Worker がメールを読む処理(入った人をアプリがどう扱うか)である。
レシピ帳そのものは、入った人のあいだで共有する。 ユーザごとに壁を立てる対象は、プロフィールとお気に入りだけである。
スキーマのコメントも「Recipes are a shared household book」と書いてある。 「ログインした人は自分のレシピだけ見える」という作りではない。
段 1: 入場(Cloudflare Access)¶
本番の想定は、カスタムドメイン home.ntatsuya.com の前に Access アプリケーションを置くことである。
IdP は Google である。許可するメールを Access のポリシーに書く。
いま README と wrangler.jsonc が前提にしている許可メールは coptis923@gmail.com のみである。
アプリのコードに許可リストは無い。
誰を通すかは Cloudflare Zero Trust 側の設定が正である。
Access を通ったリクエストには、Cloudflare が Cf-Access-Authenticated-User-Email を付ける。
Worker はこのヘッダだけを信じる。
クライアントがメールを送る項目は無い。
世帯の別の人を足すときは、Access の許可メールにその Google アカウントを追加する。 アプリのデプロイは不要である。 追加した人は、既存のコーヒー帳と料理帳を同じ権限で読めて、編集できる。
Access を外すと共有帳がむき出しになる
Worker は「Access を通った人」を区別するだけで、「このレシピの持ち主か」は見ない。 許可メールを増やした瞬間から、その人は全レシピを消せる。 公開インターネット向けの会員制アプリではない。
*.workers.dev(home-kitchen.coptis923.workers.dev)に Access を付けていない場合、HTML は取れることがある。
その場合でも /api/* はヘッダが無いと 401 になるので、レシピ本文と写真は返らない。
本番の閲覧経路はカスタムドメインに Access を付ける前提である。
段 2: アプリ内の身元¶
worker/util.ts の userEmail が今の人を決める。
| 条件 | 結果 |
|---|---|
Cf-Access-Authenticated-User-Email がある |
その値を小文字にして使う |
Host が localhost または 127.0.0.1 |
DEV_USER_EMAIL(coptis923@gmail.com) |
| どちらでもない | null → /api/* は 401 |
/api/* 以外(静的ファイル)はこの判定を通らない。
写真の URL は /api/images?key=... なので、写真も同じ 401 の対象である。
R2 のキーを知っていても、未ログインでは画像を取れない。
ローカルでは Access が無いため、開発者は常に DEV_USER_EMAIL 本人として動く。
別ユーザの見え方を試すには、wrangler.jsonc の DEV_USER_EMAIL を一時的に変えるか、Access の許可メールを増やして本番相当で見る。
共有するもの(世帯の帳面)¶
Access を通った人は、次を同じように扱える。
| 対象 | できること |
|---|---|
| コーヒー / 料理のレシピ | 一覧、詳細、作成、編集、削除 |
| 注湯、材料、手順 | 編集 |
| 写真 | 追加、並べ替え、削除、URL からの取り込み |
| 編集履歴 | 誰がいつ保存したかの閲覧 |
creator_email は「最初に作った人」の記録である。
更新しても作成者は変わらない。
一覧の作成者フィルタは、この列で絞るだけである。他人のレシピを隠す権限ではない。
削除も作成者チェックが無い。 通った人なら、サンプルを含めてどのレシピも消せる。
同時編集は権限ではなく競合である。
保存時に baseUpdatedAt を送り、D1 の updated_at と一致しなければ 409 で弾く。
メッセージは「他の人が先に保存しました」。
ユーザごとに分かれるもの¶
メールを主キーまたは一部キーにしているものだけが、人を分ける。
| 対象 | キー | 他の人から見えるか |
|---|---|---|
| プロフィール(表示名、テーマ、アバター) | profiles.email |
表示名とアバターは編集履歴や作成者の顔として見える。テーマは本人の画面だけ |
| お気に入り | favorites.email |
見えない。ホームのお気に入りは今の人の行だけ |
| 編集履歴の「誰が」 | recipe_edits.editor_email |
見える。共有帳の監査ログ |
ホームの「履歴」はサーバに無い。
端末の localStorage なので、同じ Google アカウントでも端末が違えば履歴は共有されない。
操作と範囲¶
いまの人のメールを email とする。
| 操作 | 範囲 |
|---|---|
GET /api/me、PUT /api/me |
email のプロフィールだけ |
PUT / DELETE /api/favorites/... |
email の行だけ |
GET /api/coffee、GET /api/cooking |
全レシピ。creator は任意フィルタ |
PUT / DELETE レシピ |
全レシピ。作成者で拒否しない |
GET /api/edits/... |
そのレシピの履歴(最大50件) |
GET /api/creators |
作成者または編集者として出たメールの一覧 |
POST /api/images |
認証済みなら誰でもアップロード。キーは UUID |
ロール(管理者 / 閲覧のみ)は無い。 世帯内の「自分のページだけ」も無い。 分けたいデータは、Access の許可メールを増やさないか、別アプリにする。
身元の流れ¶
flowchart LR
G[Google アカウント]
A[Access 許可リスト]
H["Cf-Access-Authenticated-User-Email"]
W[Worker userEmail]
P[profiles / favorites]
R[共有のレシピ]
G --> A
A -->|通ったときだけ| H --> W
W --> P
W --> R
Access で弾かれた人は Worker に届かない。 届いた人は、プロフィールだけ自分、レシピは全員、という非対称になる。