全体構成¶
ブラウザは静的なクライアントを読み、同じオリジンの /api/* を Worker が処理する。
HTML と JS は Worker のアセット、永続データは D1 と R2 である。
flowchart TB
subgraph edge [入口]
BR[ブラウザ / PWA]
CA[Cloudflare Access]
end
subgraph cf [Cloudflare]
WK[Worker home-kitchen]
D1[(D1 home-kitchen)]
R2[(R2 home-kitchen-images)]
end
BR --> CA --> WK
WK --> D1
WK --> R2
/api/* だけ Worker を先に走らせる。
それ以外は SPA の index.html に落とす。設定は wrangler.jsonc の assets.run_worker_first である。
技術スタック¶
いま動いているものだけを書く。
| 層 | 採用 | 役割 |
|---|---|---|
| ランタイム | Node 22(.nvmrc は 22.23.2) |
ローカルと CI の Node |
| クライアント | React 19、React Router 7、Vite 8 | UI とルーティング |
| UI | Tailwind CSS 4、shadcn、lucide | 見た目と部品 |
| API | Hono 4 on Cloudflare Workers | /api/* |
| リレーショナル | Cloudflare D1(SQLite) | レシピ、プロフィール、お気に入り、編集履歴 |
| オブジェクト | Cloudflare R2 | 写真とアバター |
| 認証ゲート | Cloudflare Access(Zero Trust) | アプリ全体の入場。Google ログイン |
| 配布 | Wrangler、GitHub Actions | main への push でデプロイ |
| インストール | vite-plugin-pwa | ホーム画面の recipe |
クライアントと Worker で共有する型と小さな純関数は shared/ にある。
クライアント固有の状態(最近開いた ID、一覧の表示サイズ、タイマーの音設定)は localStorage である。
リクエストの流れ¶
- ブラウザが
home.ntatsuya.comを開く - Access が Google アカウントを確認し、通ったリクエストに
Cf-Access-Authenticated-User-Emailを付ける - 静的アセットはそのまま返す
/api/*は Worker がヘッダからメールを取り、無ければ 401 にする- 以後の API はそのメールを「今の人」として使う
ローカル(localhost / 127.0.0.1)では Access が無い。
Worker は DEV_USER_EMAIL(いまは coptis923@gmail.com)を今の人にする。
コンポーネントの置き場¶
| パス | 責務 |
|---|---|
src/ |
画面。API は src/lib/api.ts だけが呼ぶ |
worker/index.ts |
/api/* の認証ミドルウェアとルート接続 |
worker/coffee.ts / worker/cooking.ts |
レシピ CRUD |
worker/images.ts |
画像の upload、URL 取込、取得、削除 |
worker/me.ts |
表示名、テーマ、アバター |
worker/favorites.ts |
お気に入り(メール単位) |
worker/edits.ts |
編集履歴の記録と一覧 |
migrations/ |
D1 の正本 |
Worker のバインディング名は DB と IMAGES である。
データベース名は home-kitchen、バケット名は home-kitchen-images である。
クライアントで完結するもの¶
次はサーバに無い。端末の中だけである。
- 最近開いたレシピ(ホームの履歴)
- 一覧のカードサイズ
- タイマーの音と振動のオンオフ
- ホームで最後に見ていた種別(コーヒー / 料理)
別端末では共有されない。