ご招待フォーム — 運用 / デプロイ
apps/invite-app (Worker invite-web) の開発・デプロイ・環境変数・メール送信設定。 本アプリは公開フォームのみ (認証なし)。招待リンク・文面の設定や履歴閲覧は別アプリ tied-workspace-admin が担う。
バインディング / 環境変数
apps/invite-app/wrangler.toml で定義する。型は worker-configuration.d.ts。
| 種別 | 名前 | 用途 |
|---|---|---|
| D1 | INVITE_DB (tied-workspace) | invite_settings (読取) / invitations (書込) |
| send_email | INVITE_EMAIL | 招待リンクのメール配信 (Cloudflare Email Routing) |
| var | INVITE_EMAIL_FROM | 送信元アドレス (例: no-reply@tied-workspace.com) |
| var | INVITE_EMAIL_FROM_NAME | 送信元表示名 |
| var | TURNSTILE_SITE_KEY | Turnstile (ボット対策) のサイトキー (公開値) |
| secret | TURNSTILE_SECRET_KEY | Turnstile のシークレットキー (siteverify 用) |
認証は持たないが、公開フォームのボット/スパム対策として Cloudflare Turnstile の シークレットキーのみ secret として設定する。
cd apps/invite-app
bunx wrangler secret put TURNSTILE_SECRET_KEYローカル開発では apps/invite-app/.dev.vars に置く (コミットしない)。テスト用なら Cloudflare の常に成功するテストキー TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA が使える。
ローカル開発
# 依存インストール (リポジトリルート)
bun install
# D1 のローカル状態にスキーマを適用 (Miniflare)
bunx wrangler d1 migrations apply tied-workspace --local \
--persist-to apps/invite-app/.wrangler/state -c packages/d1/wrangler.toml
# 招待設定を投入 (admin アプリ未整備の間は手動で 1 行入れる)
bunx wrangler d1 execute tied-workspace --local \
--persist-to apps/invite-app/.wrangler/state -c packages/d1/wrangler.toml \
--command "INSERT INTO invite_settings (id, invite_url, email_subject, email_body, updated_at) VALUES ('default', 'https://join.slack.com/t/...', 'TiedWorkspace へのご招待', '{{name}} 様\n\n{{inviteUrl}}', '2026-06-14T00:00:00Z')"
# 開発サーバ
bun run --filter @tied-workspace/invite-app devinvite_settings に招待リンクが入るとフォームが受付可能になる (未設定の間はフォーム送信を無効化して受付停止メッセージを出す)。本番では tied-workspace-admin から設定する。
デプロイ
bun run --filter @tied-workspace/invite-app deploy # react-router build && wrangler deployCI は .github/workflows/deploy-invite-app.yml。PR で型チェック + ビルド、main への push で 本番デプロイする。D1 スキーマの適用はアプリと独立した deploy-d1.yml が packages/d1/** の変更で行う (スキーマ変更を伴う場合は migration 側を先にマージ・適用する)。
公開ドメイン invite.tied-workspace.com は Cloudflare 側でカスタムドメインの紐付けが必要。
ボット対策 (Cloudflare Turnstile)
公開フォームは未認証でメール配信を起動できるため、送信時に Cloudflare Turnstile の トークンを検証してボット/スパムを弾く。
- フロント: フォームに Turnstile widget (
class="cf-turnstile") を描画する (スクリプトはapp/routes/index.tsxが読み込む)。サイトキーは varTURNSTILE_SITE_KEY。 - サーバー: action が送信時に
cf-turnstile-responseトークンをhttps://challenges.cloudflare.com/turnstile/v0/siteverifyで検証する (app/lib/turnstile.server.ts)。失敗したら同意記録もメール配信もしない。
本番では Cloudflare ダッシュボードで Turnstile ウィジェットを作成し、 発行されたサイトキーを wrangler.toml の TURNSTILE_SITE_KEY に、シークレットキーを secret TURNSTILE_SECRET_KEY に設定する。リポジトリの既定値は常に成功するテストキーのため、 本番では必ず差し替えること (差し替え忘れるとボット対策が無効になる)。
メール送信 (重要な制約)
配信は Cloudflare Email Routing の send_email バインディング (cloudflare:email) を使う (app/lib/email.server.ts)。
send_email の宛先制約
send_email バインディングは、宛先 (To) が Email Routing で「検証済みの宛先アドレス」で ある場合にのみ送信できる。任意の招待者メールアドレスへそのままは届かない。 本番で外部の招待者へ確実に届けるには、次のいずれかが必要:
- 送信ドメイン (
tied-workspace.com等) を Cloudflare Email Routing に登録し、 DNS (MX / SPF / DKIM / DMARC) を整える。検証済み宛先のみに配信する運用にするか、 - 任意宛先へのトランザクションメールに対応した外部プロバイダ (Resend 等) へ切り替える。 その場合も
EmailSenderポートを差し替えるだけでよく、ユースケースや UI は変更不要。
送信に失敗した同意は invitations.status = 'failed' (および email_error) として記録に残る。 tied-workspace-admin の履歴から失敗を把握できる (再送は候補者の再送信、または将来の再送 UI で対応)。
利用規約の改定
規約本文は apps/invite-app/app/lib/terms.ts が正本。改定したら本文を更新し、 必ず TERMS_VERSION を上げる。以後の同意記録には新しいバージョンが保存され、 どの版に同意したかを invitations.terms_version で追跡できる。